天天看点

nginx配置ssl加密(单/双向认证、部分https)1. 全站ssl2. 部分页面ssl3. 实现双向ssl认证

nginx下配置ssl本来是很简单的,无论是去认证中心买ssl安全证书还是自签署证书,但最近公司oa的一个需求,得以有个机会实际折腾一番。一开始采用的是全站加密,所有访问http:80的请求强制转换(rewrite)到https,后来自动化测试结果说响应速度太慢,https比http慢慢30倍,心想怎么可能,鬼知道他们怎么测的。所以就试了一下部分页面https(不能只针对某类动态请求才加密)和双向认证。下面分节介绍。

默认nginx是没有安装ssl模块的,需要编译安装nginx时加入<code>--with-http_ssl_module</code>选项。

提示:nignx到后端服务器由于一般是内网,所以不加密。

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

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

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

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

<code>ssl_ciphers</code>选择加密套件,不同的浏览器所支持的套件(和顺序)可能会不同。这里指定的是openssl库能够识别的写法,你可以通过 <code>openssl -v cipher 'rc4:high:!anull:!md5'</code>(后面是你所指定的套件加密算法) 来看所支持算法。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

继续阅读