Nginx 配置:安全

SSL/TLS 安全评估报告

可以用来检测自己部署的网站安全性情况,本文介绍一些 Nginx 常用的安全防护手段

全局 TLS 加密

这一点就不用说了,如果没套上 TLS 也不用谈什么安全了

证书的选择上我倾向于基于 secp384r1 椭圆曲线的 ec-384 证书,申请也很简单,可以参考我之前写的 Nginx 配置:证书

server 在 80 端口强制重写 HTTPS

80 端口其实是个假端口,仅负责将所有合法 HTTP 请求转发至 443 端口并拒绝所有不可接受的请求

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
listen [::]:80;
server_name myserver.com api.myserver.com cdn.myserver.com;
return 301 https://$host$request_uri;
}

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 400;
}

将可以接受的域名重写为HTTPS,交给Stream进行分流

我不允许任何 HTTP 流量请求,没人喜欢裸奔

隐藏一些路径

这里隐藏了开头是点的路径,注意返回 404 而不是 403,以免引起攻击者注意

1
2
3
4
5
server {
location ~ /\. {
return 404;
}
}

开启 HSTS

HTTP Strict Transport Security (HSTS) is a policy mechanism that helps to protect websites against man-in-the-middle attacks such as protocol downgrade attacks[1] and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should automatically interact with it using only HTTPS connections, which provide Transport Layer Security (TLS/SSL), unlike the insecure HTTP used alone. HSTS is an IETF standards track protocol and is specified in RFC 6797.

Wikipedia

添加 HSTS Header 防止 301 劫持,这一行也可以加到 http 全局配置里面

1
2
3
server {
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}

拒绝 TLS 握手

显然 Nginx 可以通过配置来实现域名白名单访问,也就是说只有我配置的域名可以访问到我的服务

那么,如果知道 IP 能不能反查出域名呢?

一个常见的配置错误

我在 Nginx 配置:基于 SNI 的分流 这篇文章中提到可以使用 Nginx 的 ngx_stream_module 实现基于 SNI 的分流,同时拒绝非法访问

那么如果不使用 stream 模块,按照传统 HTTP 配置 server_name 进行分流会怎么样?

看下面的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

server {
listen 443 ssl;
server_name api.myserver.com;

return 204;
}

server {
listen 443 ssl;
server_name cdn.myserver.com;

return 204;
}

server {
listen 443 ssl;
server_name _;

return 400;
}

当我仅知道 IP 时,我访问 https://IP,这时 Nginx 的行为是与配置第一个 server 尝试 TLS 握手

TLSv1.2 过程如下:

  1. 经过 TCP 握手,建立 TCP 连接
  2. Client 发送 Hello
  3. Server 发送 Hello 和证书(其中包含了主机名 api.myserver.com)

随后 TLS 握手失败

此时我便得知 api.myserver.com 是该 IP 的一个合法域名

更加安全的方法

预读 SNI,非法请求直接拒绝握手

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
28
29
30
31
32
33
34
35
36
stream {
map $ssl_preread_server_name $backend_name {
api.myserver.com api;
default bad;
}

upstream api {
server 127.0.0.1:777;
}

upstream bad {
server 127.0.0.1:400;
}

server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
}
}

http {
server {
listen 777 ssl;

# do something
}

server {
listen 400 ssl;

# 拒绝握手
ssl_reject_handshake on;
}
}

合理选择加密协议与套件

ssl_protocols

所有现代浏览器和软件都已经支持 TLSv1.3,如果需要兼容几年前的老浏览器,推荐加上 TLSv1.2

1
2
3
http {
ssl_protocols TLSv1.3 TLSv1.2;
}

ssl_ciphers

推荐开启使用 server 端 cipher 顺序

1
2
3
4
5
http {
ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
}

一些请求头

1
2
3
4
5
http {
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
}

其他配置参考

Nginx 正确安装姿势

Nginx 配置:业务部分

Nginx 配置:证书

Nginx 配置:基于 SNI 的分流

Nginx 配置:安全