Nginx 配置之安全篇

一、隐藏不必要的信息

大家可以看一下我的博客请求响应头,有这么一行 server: nginx,说明我用的是 Nginx 服务器,但并没有具体的版本号。由于某些 Nginx 漏洞只存在于特定的版本,隐藏版本号可以提高安全性。这只需要在配置里加上这个就可以了:

server_tokens off;

如果想要更彻底隐藏所用 Web Server,可以修改 Nginx 源码,把 Server Name 改掉再编译,具体步骤可以自己搜索。需要提醒的是:如果你的网站支持 SPDY,只改动网上那些文章写到的地方还不够,跟 SPDY 有关的代码也要改。更简单的做法是改用 Tengine 这个 Nginx 的增强版,并指定 server_tag 为 off 或者任何想要的值就可以了。另外,既然想要彻底隐藏 Nginx,404、500 等各种出错页也需要自定义。

同样,一些 WEB 语言或框架默认输出的 x-powered-by 也会泄露网站信息,他们一般都提供了修改或移除的方法,可以自行查看手册。如果部署上用到了 Nginx 的反向代理,也可以通过 proxy_hide_header 指令隐藏它:
proxy_hide_header X-Powered-By;

二、禁用非必要的方法

由于我的博客只处理了 GET、POST 两种请求方法,而 HTTP/1 协议还规定了 TRACE 这样的方法用于网络诊断,这也可能会暴露一些信息。所以我针对 GET、POST 以及 HEAD 之外的请求,直接返回了 444 状态码(444 是 Nginx 定义的响应状态码,会立即断开连接,没有响应正文)。具体配置是这样的:

if ($request_method !~ ^(GET|HEAD|POST)$ ) {
    return 444;
}

三、合理配置响应头

我的博客是由自己用 ThinkJS 写的 Node 程序提供服务,Nginx 通过 proxy_pass 把请求反向代理给 Node 绑定的 IP 和端口。在最终输出时,我给响应增加了以下头部:

add_header Strict-Transport-Security "max-age=31536000";
add_header X-Frame-Options deny;
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://a.disquscdn.com; img-src 'self' data: https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; frame-src https://disqus.com";
  • Strict-Transport-Security(简称为 HSTS)可以告诉浏览器,在指定的 max-age 内,始终通过 HTTPS 访问我的博客。即使用户自己输入 HTTP 的地址,或者点击了 HTTP 链接,浏览器也会在本地替换为 HTTPS 再发送请求。另外由于我的证书不支持多域名,我没有加上 includeSubDomains。关于 HSTS 更多信息,可以查看我之前的介绍。
  • X-Frame-Options 用来指定此网页是否允许被 iframe 嵌套,deny 就是不允许任何嵌套发生。关于这个响应头的更多介绍可以看这里。
  • X-Content-Type-Options 用来指定浏览器对未指定或错误指定 Content-Type 资源真正类型的猜测行为,nosniff 表示不允许任何猜测。这部分内容更多介绍见这里。
  • Content-Security-Policy(简称为 CSP)用来指定页面可以加载哪些资源,主要目的是减少 XSS 的发生。我允许了来自本站、disquscdn 的外链 JS,还允许内联 JS,以及在 JS 中使用 eval;允许来自本站和 google 统计的图片,以及内联图片(Data URI 形式);允许本站外链 CSS 以及内联 CSS;允许 iframe 加载来自 disqus 的页面。对于其他未指定的资源,都会走默认规则 self,也就是只允许加载本站的。关于 CSP 的详细介绍请看这里。

之前的博客中,我还介绍过 X-XSS-Protection 这个响应头,也可以用来防范 XSS。不过由于有了 CSP,所以我没配置它。需要注意的是,以上这些响应头现代浏览器才支持,所以并不是说加上他们,网站就可以不管 XSS,万事大吉了。但是鉴于低廉的成本,还是都配上。

四、HTTPS 安全配置

启用 HTTPS 并正确配置了证书,意味着数据传输过程中无法被第三者解密或修改。有了 HTTPS,也得合理配置好 Web Server,才能发挥最大价值。我的博客关于 HTTPS 这一块有以下配置:

ssl_certificate /home/jerry/ssl/server.crt;
ssl_certificate_key /home/jerry/ssl/server.key;
ssl_dhparam /home/jerry/ssl/dhparams.pem;

ssl_ciphers CHACHA20:EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES128-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA128:DHE-RSA-AES128-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA128:ECDHE-RSA-AES128-SHA384:ECDHE-RSA-AES128-SHA128:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA384:AES128-GCM-SHA128:AES128-SHA128:AES128-SHA128:AES128-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

ssl_prefer_server_ciphers on;

最终效果是我的博客在 ssllabs 的测试中达到了 A+,如下图:

QQ截图20150910085215

如何配置 ssl_ciphers 可以参考这个网站。需要注意的是,这个网站默认提供的加密方式安全性较高,一些低版本客户端并不支持,例如 IE9-、Android2.2- 和 Java6-。如果需要支持这些老旧的客户端,需要点一下网站上的「Yes, give me a ciphersuite that works with legacy / old software」链接。

另外,我在 ssl_ciphers 最开始加上了 CHACHA20,这是因为我的 Nginx 支持了 CHACHA20_POLY1305 加密算法,这是由 Google 开发的新一代加密方式,它有两方面优势:更好的安全性和更好的性能(尤其是在移动和可穿戴设备上)。下面有一张移动平台上它与 AES-GCM 的加密速度对比图(via):

QQ截图20150910085235

启用 CHACHA20_POLY1305 最简单的方法是在编译 Nginx 时,使用 LibreSSL 代替 OpenSSL。下面是用 Chrome 访问我的博客时,点击地址栏小锁显示的信息,可以看到加密方式使用的就是 CHACHA20_POLY1305:

QQ截图20150910085318

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>