Nginx配置HTTPS与HSTS

以下以一个不存在的域名example.qidong.name为例,展示在Nginx中配置HTTPSHSTS的过程。 本文代码追求可读性,减少一些无关的配置,仅供参考。

HTTPS

当然,首先假设你已经有了私钥(example.key)与证书(example.pem)文件。

Nginx配置SSL/TLS

如果了解原理,理解思路就会很简单。

从HTTP到HTTPS,逻辑上相当于在应用层和传输层之间,额外增加一个安全层。 对一个Web应用、网站来说,从HTTP到HTTPS,它本身可以不做任何修改、甚至可以完全不知情。

HTTPS的默认端口是443,给Nginx新增一个443端口的监听。 并且,在其中开启ssl on,添加相应配置。 最后,仍然用rootproxy_pass等方式,把请求交给真正需要响应的地方。

server {
    listen 443;
    listen [::]:443;
    server_name example.qidong.name;

    ssl on;
    ssl_certificate /etc/nginx/ssl/example.pem;
    ssl_certificate_key /etc/nginx/ssl/example.key;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;

    location / {
        proxy_pass http://localhost:12345;
    }
}

新增上面的配置后,Nginx支持https://example.qidong.name/形式的访问。 并且最终通过反向代理,把网络请求交给服务器里12345端口。

两个listen,是同时对IPv4和IPv6进行监听,统一支持。

不过,这时如果访问http://example.qidong.name/,仍然是允许的。 如果要确保安全性,限定必须使用HTTPS来访问,有两种办法。 一是完全不提供http://example.qidong.name/的访问,不在80端口监听、响应example.qidong.name。 这种方法用得比较少,因为考虑到旧的链接、浏览器自动补全HTTP等因素,会导致网页不可达,令用户困惑。

所以,通常用的是第二种方案。 利用重定向,让HTTP的链接自动跳转为HTTPS。

HTTP重定向到HTTPS

server {
    listen 80;
    listen [::]:80;
    server_name example.qidong.name;
    return 301 https://$server_name$request_uri;
}

添加、或修改以上代码,即可让用户访问http://example.qidong.name/时, 自动跳转到https://example.qidong.name/

有些情况下,SSL/TLS这一层,是包在Nginx外的, 比如,某些非常自动化处理HTTPS配置与证书的商业环境。 或者,也可以理解成外面还有一层Nginx,自动做了前面ssl on的处理,但不能手动改配置。 在这些环境下,Nginx对外提供的80端口,就必须兼备跳转到HTTPS和提供内容两个功能。

以下代码,通过http_x_forwarded_proto进行识别,分别处理。 关键在if那三行。

server {
    listen 80;
    listen [::]:80;
    server_name example.qidong.name;
    if ($http_x_forwarded_proto = 'http') {
        return 301 https://$host$request_uri;
    }

    location / {
        proxy_pass http://localhost:12345;
    }
}

HSTS

SSL剥离攻击是中间人攻击的一种,由Moxie Marlinspike于2009年发明。 他在当年的黑帽大会上发表的题为《New Tricks For Defeating SSL In Practice》的演讲中将这种攻击方式公开。 SSL剥离的实施方法是阻止浏览器与服务器创建HTTPS连接。

HTTP重定向到HTTPS,仍然是有安全隐患的,那就是SSL剥离攻击。 因此而发展起来的应对,则是HSTS

HSTS

在Nginx上,要添加HSTS的支持非常简单。 只需要在443的监听中,添加一行add_header。 其中,max-age=31536000,就是有效期一年。

server {
    listen 443;
    server_name example.qidong.name;

    ssl on;
    ...

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

    location / {
        ...
    }
}

一些优化

    keepalive_timeout   70;

    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets on;
    ssl_stapling        on;
    ssl_stapling_verify on;

这些都是关于HTTPS,Nginx提供的优化手段。 关于cache、timeout,大概是以空间换时间、用缓存降流量,尽量避免多次请求、反复验证。 而stapling,则是优化证书验证的有效方法。

浏览器在建立HTTPS链接时,会先在线验证CA证书的有效性,阻塞SSL/TLS的握手,拖慢整体响应速度。 这就是OCSP,全称Online Certificate Status Protocol,译为在线证书状态协议。 而OCSP stapling,就是让服务端来进行OCSP,并且缓存证书颁发机构的响应,让浏览器跳过在线检查。 本来一次HTTPS请求,正常是浏览器、服务器、证书颁发机构的三角关系,优化后,大部分情况下变成了浏览器和服务器的双边关系。

相比HTTP,HTTPS对服务器的性能开销剧增。 但如果做好一些优化,可以缓解不少。

另外,还有安全方面的优化。 SSL协议总体不安全,全面弃用。 而TLS协议的安全性,则依赖于ciphers。

ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

这是孤见过最长的ciphers列表,应该能更安全一些吧。 虽然看不懂,但是好厉害的样子!

SSL测试

一切配置完毕后,可以去ssllabs。 输入域名,一次点击,可以进行多方面的测试。

参考


相关笔记