yeskery

Nginx 配置 SSL 加密(单双向认证、部分HTTPS)

SSL/TLS 介绍

  • SSL:(Secure Socket Layer,安全套接字层),为Netscape所研发,用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取。当前版本为3.0。它已被广泛地用于Web浏览器与服务器之间的身份认证和加密数据传输。
    SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。

  • TLS:(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。
    TLS 1.0是IETF(Internet Engineering Task Force,Internet工程任务组)制定的一种新的协议,它建立在SSL 3.0协议规范之上,是SSL 3.0的后续版本,可以理解为SSL 3.1,它是写入了 RFC 的。该协议由两层组成: TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。较低的层为 TLS 记录协议,位于某个可靠的传输协议(例如 TCP)上面。

SSL/TLS协议提供的服务主要有:

  1. 认证用户和服务器,确保数据发送到正确的客户机和服务器;
  2. 加密数据以防止数据中途被窃取;
  3. 维护数据的完整性,确保数据在传输过程中不被改变。

全站 SSL

全站做ssl是最常见的一个使用场景,默认端口443,而且一般是单向认证。

  1. server {
  2. listen 443;
  3. server_name example.com;
  4. root /apps/www;
  5. index index.html index.htm;
  6. ssl on;
  7. ssl_certificate ../SSL/ittest.pem;
  8. ssl_certificate_key ../SSL/ittest.key;
  9. # ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
  10. # ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
  11. # ssl_prefer_server_ciphers on;
  12. }

如果想把http的请求强制转到https的话:

  1. server {
  2. listen 80;
  3. server_name example.me;
  4. rewrite ^ https://$server_name$request_uri? permanent;
  5. ### 使用return的效率会更高
  6. # return 301 https://$server_name$request_uri;
  7. }

ssl_certificate证书其实是个公钥,它会被发送到连接服务器的每个客户端,ssl_certificate_key私钥是用来解密的,所以它的权限要得到保护但nginx的主进程能够读取。当然私钥和证书可以放在一个证书文件中,这种方式也只有公钥证书才发送到client。

ssl_protocols指令用于启动特定的加密协议,nginx在1.1.13和1.0.12版本后默认是ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2,TLSv1.1与TLSv1.2要确保OpenSSL >= 1.0.1 ,SSLv3 现在还有很多地方在用但有不少被攻击的漏洞。

ssl_ciphers选择加密套件,不同的浏览器所支持的套件(和顺序)可能会不同。这里指定的是OpenSSL库能够识别的写法,你可以通过 openssl -v cipher ‘RC4: HIGH: !aNULL:!MD5’(后面是你所指定的套件加密算法) 来看所支持算法。

ssl_prefer_server_ciphers on设置协商加密算法时,优先使用我们服务端的加密套件,而不是客户端浏览器的加密套件。

https优化参数

  • ssl_session_cache shared: SSL:10m; : 设置ssl/tls会话缓存的类型和大小。如果设置了这个参数一般是sharedbuildin可能会参数内存碎片,默认是none,和off差不多,停用缓存。如shared :SSL :10m表示我所有的nginx工作进程共享ssl会话缓存,官网介绍说1M可以存放约4000个sessions。 详细参考serverfault上的问答ssl_session_cache。

  • ssl_session_timeout : 客户端可以重用会话缓存中ssl参数的过期时间,内网系统默认5分钟太短了,可以设成30m即30分钟甚至4h

设置较长的keepalive_timeout也可以减少请求ssl会话协商的开销,但同时得考虑线程的并发数了。

提示:在生成证书请求csr文件时,如果输入了密码,nginx每次启动时都会提示输入这个密码,可以使用私钥来生成解密后的key来代替,效果是一样的,达到免密码重启的效果:

  1. openssl rsa -in ittest.key -out ittest_unsecure.key

导入证书

如果你是找一个知名的ssl证书颁发机构如VeriSign、Wosign、StartSSL签发的证书,浏览器已经内置并信任了这些根证书,如果你是自建C或获得二级CA授权,都需要将CA证书添加到浏览器,这样在访问站点时才不会显示不安全连接。各个浏览的添加方法不在本文探讨范围内。

部分页面SSL

一个站点并不是所有信息都是非常机密的,如网上商城,一般的商品浏览可以不通过https,而用户登录以及支付的时候就强制经过https传输,这样用户访问速度和安全性都得到兼顾。

但是请注意不要理解错了,是对页面加密而不能针对某个请求加密,一个页面或地址栏的URL一般会发起许多请求的,包括css/png/js等静态文件和动态的java或php请求,所以要加密的内容包含页面内的其它资源文件,否则就会出现http与https内容混合的问题。在http页面混有https内容时,页面排版不会发生乱排现象;在https页面中包含以http方式引入的图片、js等资源时,浏览器为了安全起见会阻止加载。

下面是只对example.com/account/login登录页面进行加密的栗子:

  1. root /apps/www;
  2. index index.html index.htm;
  3. server {
  4. listen 80;
  5. server_name example.com;
  6. location ^~ /account/login {
  7. rewrite ^ https://$server_name:443$request_uri? permanent;
  8. }
  9. location / {
  10. proxy_pass http://localhost:8080;
  11. ### Set headers ####
  12. proxy_set_header Host $host;
  13. proxy_set_header X-Real-IP $remote_addr;
  14. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  15. proxy_redirect off;
  16. }
  17. }
  18. server {
  19. listen 443 ssl;
  20. server_name example.com;
  21. ssl on;
  22. ssl_certificate ../SSL/ittest.pem;
  23. ssl_certificate_key ../SSL/ittest.key;
  24. ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
  25. ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
  26. ssl_prefer_server_ciphers on;
  27. location ^~ /account/login {
  28. proxy_pass http://localhost:8080;
  29. proxy_set_header Host $host;
  30. proxy_set_header X-Real-IP $remote_addr;
  31. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  32. proxy_redirect off;
  33. ### Most PHP, Python, Rails, Java App can use this header -> https ###
  34. proxy_set_header X-Forwarded-Proto $scheme;
  35. }
  36. location / {
  37. rewrite ^ http://$server_name$request_uri? permanent;
  38. }
  39. }

关于rewrite与location的写法参考这里。当浏览器访问http://example.com/account/login.xx时,被301到https://example.com/account/login.xx,在这个ssl加密的虚拟主机里也匹配到/account/login,反向代理到后端服务器,后面的传输过程是没有https的。这个login.xx页面下的其它资源也是经过https请求nginx的,登录成功后跳转到首页时的链接使用http,这个可能需要开发代码里面控制。

  • 上面配置中使用了proxy_set_header X-Forwarded-Proto $scheme,在jsp页面使用request.getScheme()得到的是https 。如果不把请求的$scheme协议设置在header里,后端jsp页面会一直认为是http,将导致响应异常。

  • ssl配置块还有个与不加密的80端口类似的location /,它的作用是当用户直接通过https访问首页时,自动跳转到不加密端口,你可以去掉它允许用户这样做。

实现双向SSL认证

上面的两种配置都是去认证被访问的站点域名是否真实可信,并对传输过程加密,但服务器端并没有认证客户端是否可信。(实际上除非特别重要的场景,也没必要去认证访问者,除非像银行U盾这样的情况)

要实现双向认证HTTPS,nginx服务器上必须导入CA证书(根证书/中间级证书),因为现在是由服务器端通过CA去验证客户端的信息。还有必须在申请服务器证书的同时,用同样的方法生成客户证书。取得客户证书后,还要将它转换成浏览器识别的格式(大部分浏览器都认识PKCS12格式):

  1. openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12

然后把这个client.p12发给你相信的人,让它导入到浏览器中,访问站点建立连接的时候nginx会要求客户端把这个证书发给自己验证,如果没有这个证书就拒绝访问。

同时别忘了在 nginx.conf 里配置信任的CA:(如果是二级CA,请把根CA放在后面,形成CA证书链)

  1. proxy_ignore_client_abort on
  2. ssl on;
  3. ...
  4. ssl_verify_client on;
  5. ssl_verify_depth 2;
  6. ssl_client_certificate ../SSL/ca-chain.pem;
  7. # 在双向location下加入:
  8. proxy_set_header X-SSL-Client-Cert $ssl_client_cert;

拓展:使用geo模块

nginx默认安装了一个ngx_http_geo_module,这个geo模块可以根据客户端IP来创建变量的值,用在如来自172.29.73.0/24段的IP访问login时使用双向认证,其它段使用一般的单向认证。

  1. geo $duplexing_user {
  2. default 1;
  3. include geo.conf; # 注意在0.6.7版本以后,include是相对于nginx.conf所在目录而言的
  4. }

语法 geo [$address] $variable { … },位于http段,默认地址是$reoute_addr,假设 conf/geo.conf 内容:

  1. 127.0.0.1/32 LOCAL; # 本地
  2. 172.29.73.23/32 SEAN; # 某个IP
  3. 172.29.73.0/24 1; # IP段,可以按国家或地域定义后面的不同的值

需要配置另外一个虚拟主机server{ssl 445},里面使用上面双向认证的写法,然后在80或443里使用变量$duplexing_user去判断,如果为1就rewrite到445,否则rewrite到443。具体用法可以参考nginx geo使用方法

参考

  1. Nginx部署部分https与部分http
  2. Linux+Nginx/Apache/Tomcat新增SSL证书,开启https访问教程
  3. SSL & SPDY 已全面部署
  4. SSL证书与Https应用部署小结
  5. ngx_http_ssl_module docs
  6. Optimizing HTTPS on Nginx
  7. http://zhangge.net/4861.html
  8. http://blog.chinaunix.net/uid-192074-id-3135733.html

文章转载自:http://seanlook.com/2015/05/28/nginx-ssl/

评论

发表评论 点击刷新验证码

提示

该功能暂未开放