Nginx 基础配置 安全性配置

Nginx配置error_page 404 500等自定义的错误页面

  • 1.创建自己的404.html页面

  • 2.更改nginx.conf在http定义区域加入:

1
2
3
4
5
server{
...
fastcgi_intercept_errors on;
...
}
  • 3.更改nginx.conf(或单独网站配置文件)中在server 区域加入:
1
2
3
4
5
6
7
8
server{
...
error_page 400 401 402 403 404 405 408 410 412 413 414 415 500 501 502 503 504 506 /404.html;
location = /404.html {
root /alidata/www/phpwind/error;
}
...
}
  • 4.更改后重启nginx,测试nginx.conf正确性:
    1
    nginx -t
  • 5.502 等错误可以用同样的方法来配置。
1
2
3
4
5
6
7
8
server{
...
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /alidata/www/phpwind/error;
}
...
}

Nginx隐藏版本号的安全性与方法

隐藏原因:Nginx某些版本有漏洞,暴露出来容易被攻击者利用,隐藏起来更安全

隐藏版本号

nginx.conf中去掉下面注释,或者添加这一行

1
2
3
4
5
http{
...
server_tokens off
...
}

如果是转发给php-fpm ,需要编辑fastcgi.conf,一般在nginx.conf 同层
找到:

1
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

改为:

1
fastcgi_param SERVER_SOFTWARE nginx;

编译源码返回自定义的server

修改src/http/ngx_http_header_filter_module.c 中的48行

1
static char ngx_http_server_string[] = "Server: nginx" CRLF;

把其中的nginx改为我们自己想要的文字即可,笔者就改为了GFW.

修改src/core/nginx.h 定位到13-14行

1
2
3
#define nginx_version      2000000
#define NGINX_VERSION "2.0"
#define NGINX_VER "GFW/" NGINX_VERSION

Server返回的就是常量NGINX_VER

重新编译

1
make && make install

控制缓冲区溢出攻击

编辑nginx.conf,为所有客户端设置缓冲区的大小限制。

编辑和设置所有客户端缓冲区的大小限制如下:

1
2
3
4
5
6
7
8
9
10
http {
...
## Start: Size Limits & Buffer Overflows ##
client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 1k;
## END: Size Limits & Buffer Overflows ##
...
}

解释:

  • 1、client_body_buffer_size 1k-(默认8k或16k)这个指令可以指定连接请求实体的缓冲区大小。如果连接请求超过缓存区指定的值,那么这些请求实体的整体或部分将尝试写入一个临时文件。

  • 2、client_header_buffer_size 1k-指令指定客户端请求头部的缓冲区大小。绝大多数情况下一个请求头不会大于1k,不过如果有来自于wap客户端的较大的cookie它可能会大于1k,Nginx将分配给它一个更大的缓冲区,这个值可以在large_client_header_buffers里面设置。

  • 3、client_max_body_size 1k-指令指定允许客户端连接的最大请求实体大小,它出现在请求头部的Content-Length字段。
    如果请求大于指定的值,客户端将收到一个”Request Entity Too Large” (413)错误。记住,浏览器并不知道怎样显示这个错误。

  • 4、large_client_header_buffers-指定客户端一些比较大的请求头使用的缓冲区数量和大小。请求字段不能大于一个缓冲区大小,如果客户端发送一个比较大的头,nginx将返回”Request URI too large” (414)

    同样,请求的头部最长字段不能大于一个缓冲区,否则服务器将返回”Bad request” (400)。缓冲区只在需求时分开。默认一个缓冲区大小为操作系统中分页文件大小,通常是4k或8k,如果一个连接请求最终将状态转换为keep-alive,它所占用的缓冲区将被释放。
    你还需要控制超时来提高服务器性能并与客户端断开连接。按照如下编辑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    http {
    ...
    ## Start: Timeouts ##
    client_body_timeout 10;
    client_header_timeout 10;
    keepalive_timeout 5 5;
    send_timeout 10;
    ## End: Timeouts ##
    ...
    }
  • 1、client_body_timeout 10;-指令指定读取请求实体的超时时间。这里的超时是指一个请求实体没有进入读取步骤,如果连接超过这个时间而客户端没有任何响应,Nginx将返回一个”Request time out” (408)错误。

  • 2、client_header_timeout 10;-指令指定读取客户端请求头标题的超时时间。这里的超时是指一个请求头没有进入读取步骤,如果连接超过这个时间而客户端没有任何响应,Nginx将返回一个”Request time out” (408)错误。

  • 3、keepalive_timeout 5 5; – 参数的第一个值指定了客户端与服务器长连接的超时时间,超过这个时间,服务器将关闭连接。参数的第二个值(可选)指定了应答头中Keep-Alive: timeout=time的time值,这个值可以使一些浏览器知道什么时候关闭连接,以便服务器不用重复关闭,如果不指定这个参数,nginx不会在应答头中发送Keep-Alive信息。(但这并不是指怎样将一个连接“Keep-Alive”)参数的这两个值可以不相同。

  • 4、send_timeout 10; 指令指定了发送给客户端应答后的超时时间,Timeout是指没有进入完整established状态,只完成了两次握手,如果超过这个时间客户端没有任何响应,nginx将关闭连接。

限制可用的请求方法

GET和POST是互联网上最常用的方法。 Web服务器的方法被定义在RFC 2616。如果Web服务器不要求启用所有可用的方法,它们应该被禁用。下面的指令将过滤只允许GET,HEAD和POST方法:

1
2
3
4
5
6
7
8
9
server {
...
## Only allow these request methods ##
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
## Do not accept DELETE, SEARCH and other methods ##
...
}

更多关于HTTP方法的介绍

GET方法是用来请求,如文件https://www.centos.bz/index.php。
HEAD方法是一样的,除非该服务器的GET请求无法返回消息体。
POST方法可能涉及到很多东西,如储存或更新数据,或订购产品,或通过提交表单发送电子邮件。这通常是使用服务器端处理,如PHP,Perl和Python等脚本。如果你要上传的文件和在服务器处理数据,你必须使用这个方法。

拒绝一些User-Agents

你可以很容易地阻止User-Agents,如扫描器,机器人以及滥用你服务器的垃圾邮件发送者。
Nginx的444状态比较特殊,如果返回444那么客户端将不会收到服务端返回的信息,就像是网站无法连接一样

1
2
3
4
5
6
7
8
9
server {
...
## Block download agents ##
if ($http_user_agent ~* LWP::Simple|BBBike|wget|curl) {
return 444;
}
##
...
}

阻止Soso和有道的机器人:

1
2
3
4
5
6
7
8
9
server {
...
## Block some robots ##
if ($http_user_agent ~* Sosospider|YodaoBot) {
return 403;
}
##
...
}

Header头设置

通过以下设置可有效防止XSS攻击

1
2
3
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";

X-Frame-Options: 响应头表示是否允许浏览器加载frame等属性,有三个配置DENY禁止任何网页被嵌入,SAMEORIGIN只允许本网站的嵌套,ALLOW-FROM允许指定地址的嵌套

X-XSS-Protection: 表示启用XSS过滤(禁用过滤为X-XSS-Protection: 0),mode=block表示若检查到XSS攻击则停止渲染页面

X-Content-Type-Options: 响应头用来指定浏览器对未指定或错误指定Content-Type资源真正类型的猜测行为,nosniff 表示不允许任何猜测

在通常的请求响应中,浏览器会根据Content-Type来分辨响应的类型,但当响应类型未指定或错误指定时,浏览会尝试启用MIME-sniffing来猜测资源的响应类型,这是非常危险的

例如一个.jpg的图片文件被恶意嵌入了可执行的js代码,在开启资源类型猜测的情况下,浏览器将执行嵌入的js代码,可能会有意想不到的后果

另外还有几个关于请求头的安全配置需要注意

Content-Security-Policy: 定义页面可以加载哪些资源,

1
add_header Content-Security-Policy "default-src 'self'";

上边的配置会限制所有的外部资源,都只能从当前域名加载,其中default-src定义针对所有类型资源的默认加载策略,self允许来自相同来源的内容

Strict-Transport-Security: 会告诉浏览器用HTTPS协议代替HTTP来访问目标站点

1
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";

上边的配置表示当用户第一次访问后,会返回一个包含了Strict-Transport-Security响应头的字段,这个字段会告诉浏览器,在接下来的31536000秒内,当前网站的所有请求都使用https协议访问,参数includeSubDomains是可选的,表示所有子域名也将采用同样的规则

经过多层CDN之后取得原始用户的IP地址,nginx 配置根据用户的真实 IP 做连接限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
http {
...
##############
map $http_x_forwarded_for $clientRealIp {
## 没有通过代理,直接用 remote_addr
"" $remote_addr;
## 用正则匹配,从 x_forwarded_for 中取得用户的原始IP
## 例如 X-Forwarded-For: 202.123.123.11, 208.22.22.234, 192.168.2.100,...
## 这里第一个 202.123.123.11 是用户的真实 IP,后面其它都是经过的 CDN 服务器
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}

## 通过 map 指令,我们为 nginx 创建了一个变量 $clientRealIp ,这个就是 原始用户的真实 IP 地址,
## 不论用户是直接访问,还是通过一串 CDN 之后的访问,我们都能取得正确的原始IP地址
#################
## 针对原始用户 IP 地址做限制
limit_conn_zone $clientRealIp zone=TotalConnLimitZone:20m ;
limit_conn TotalConnLimitZone 50;
limit_conn_log_level notice;

## 针对原始用户 IP 地址做限制
limit_req_zone $clientRealIp zone=ConnLimitZone:20m rate=10r/s;
limit_req zone=ConnLimitZone burst=10 nodelay;
limit_req_log_level notice;
######################
...
}

nginx日志按天保存

1
2
3
4
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '

                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
1
2
3
log_format  main  '$remote_addr - $remote_user [$time_iso8601] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

将原来的time_local修改为time_iso8601,该格式日期为“2017-01-19T09:10:52+08:00”,也可以其他格式,看个人习惯

注意层次关系,这段脚本一定要加到server配置内部,且if要在access_log前面,否则set的变量将无法引用

1
2
3
4
5
6
7
8
server{
...
if ($time_iso8601 ~ '(\d{4}-\d{2}-\d{2})') {
                set $tttt $1;
        }
        access_log  logs/access-$tttt.log  main;
...
}

按yyyy-mm-dd格式截取字符串,写入指定日志文件中

执行 nginx -s reload 后则配置生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
http {
....
log_format main '$remote_addr - $remote_user [$time_iso8601] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
server {
if ($time_iso8601 ~ '(\d{4}-\d{2}-\d{2})') {
set $tttt $1;
}
access_log logs/$tttt.access.log main;
....
}
....
}

评论