Linux Nginx服务器搭建SSL/TLS(https+http/2)

首先,SSL/TLS是什么?

比如:https://www.awaimai.com/,前面是https,表明这个是https协议。

https就是http + SSL/TLS,在http外面套一个加密层,让第三方难以得到传输的明文数据。

如果用chrome访问这个站,在这个URL旁边会显示一个绿色的锁,表明这个连接是安全的

另外,境外的https还有一个附加效果,就是抵御关键字的审查,同时Google也更喜欢收录https的站点。

其实以上是在说废话,看这文章的乃萌肯定是知道此为何物才会找到这里来的啦,所以就不废话了。

由于这里主要讲搭建步骤,去CA机构注册什么的就不介绍了,个人小站推荐 StartSSLAlphaSSL ,前者可以得到免费证书。

1 生成证书签署请求文件

新建一个目录,并在这个目录中生成密钥文件(.key)和证书签署请求文件(.csr):

$ mkdir /etc/nginx/ssl
$ cd /etc/nginx/ssl
$ openssl req -new -newkey rsa:2048 -sha256 -nodes -out www.awaimai.com.csr -keyout www.awaimai.com.key

其中,

  • req:表示指定证书类型,后面一般跟-new或者-x509选项。-new说明要生成证书请求,x509说明要生成自签名证书。
  • -newkey:表示在生成证书请求或者自签名证书时自动生成密钥,本例要求生成2048位的rsa密钥。
  • -sha256:表示签名算法使用sha256。openssl默认采用sha1加密,而现代已将 sha1 加密方式认定为非安全,故使用sha2。
  • -nodes:表示认证过程不需要输入密码。
  • -out:指定证书名称。
  • -keyout:指定密钥名称。

然后会要求你输入一些信息,具体如下,从 Email Address 开始不要填写:

Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Guangdong
Locality Name (eg, city) []:Shenzhen
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Awaimai Inc.
Organizational Unit Name (eg, section) []:Web Security
Common Name (eg, YOUR name) []:www.awaimai.com
Email Address []:
A challenge password []:
An optional company name []:

Common Name 填写你要 SSL 支持的域名,如 www.awaimai.com,泛域名证书填*.awaimai.com。

填写完毕后,就会生成 www.awaimai.com.csr请求文件和www.awaimai.com.key密钥文件。

上面的信息也可以在openssl命令后加上-subj参数指定,省去证书信息的录入过程:

$ openssl req -new -newkey rsa:2048 -sha256 -nodes -out www.awaimai.com.csr -keyout www.awaimai.com.key -subj "/C=CN/ST=Guangdong/L=Shenzhen/O=Awaimai Inc./OU=Web Security/CN=www.awaimai.com"

这样第一步就完成了。

2 签发一个crt证书

使用编辑器打开刚才生成的csr文件,会看到类似如下的内容:

 -----BEGIN CERTIFICATE REQUEST-----
 MIICrjCCAZYCAQAwaTELMAkGA1UEBhMCR0IxDzANBgNVBAgMBkxvbmRvbjEPMA0G
 ......
 -----END CERTIFICATE REQUEST-----

复制里面全部内容,然后在CA机构网站上,提交csr内容的地方粘贴。

如下是 Let's Encrypt 和 AlphaSSL 的网站地址。

签发成功后,就能下载回来一个.crt文件,可能通过网页上下载,也可能通过邮件方式发送给你。

如果是邮件方式的话,最好是使用Gmail邮箱。

邮件方式的话需要自行复制里面crt文件的部分自行保存为crt文件。

crt文件类似以下的格式:

 -----BEGIN CERTIFICATE-----
 MIIETTCCAzWgAwIBAgILBAAAAAABRE7wNjEwDQYJKoZIhvcNAQELBQAwVzELMAkG
 ...
 -----END CERTIFICATE-----

保存好了后,第二步就完成了。

3 配置证书链

这一步在某些情况下不是必需的,建议先跳过这一步。

如果Chrome访问网站提示网站不可信,再配置证书链。如果已经提示安全,则不需要配置证书链。

前面推荐的两家机构所签发的证书没有直接返回证书链,需要自行配置。

用Chrome访问网站,按F12进入调试模式,选Security标签,点击View Certificate按钮查看证书。

如上,我的网站与根证书中间差了一级,上上一级才是根证书DST Root CA X3。

而浏览器和操作系统里面保存的均是可信任的根证书,中间那一层Let's Encrpt Authority X3很可能是没有的。

直接使用CA机构提供的crt证书,可能导致浏览器提示这是不可信的网站。

那如何配置证书链?其实配置这个证书链非常的简单。

首先,选择父级证书,点击【查看证书】,选【详细信息】,【 复制到文件】,到处格式用“base64 编码 X.509(.CER)”,然后导出父级证书。

同理,导出父级的父级证书。

使用文本编辑器打开crt证书,然后父级crt证书的内容粘贴到后面,像这样子:

 -----BEGIN CERTIFICATE-----
 自己的crt证书
 -----END CERTIFICATE-----
 -----BEGIN CERTIFICATE-----
 父级的crt证书
 -----END CERTIFICATE-----
 -----BEGIN CERTIFICATE-----
 父级的父级crt证书
 -----END CERTIFICATE-----

有多少层就做多少层(除了根证书那一层),然后保存为新的crt文件,如cert_chain.crt。

就完成了证书链的配置了。

4 Nginx配置

文本编辑器打开nginx配置文件,找到相应域名。

删除对80端口的监听:

    listen 80;

加入如下配置:

server {
    # ...

    # 监听443端口
    listen 443 ssl;
    # 开启ssl
    ssl on;
    # 指定域名
    server_name www.awaimai.com;
    # 指定证书
    ssl_certificate /etc/nginx/ssl/cert_chain.crt;
    # 指定密钥
    ssl_certificate_key /etc/nginx/ssl/www.awaimai.com.key;

    # 使用HSTS(HTTP Strict Transport Security)策略,强制浏览器总是使用HTTPS连接,
    # 这样攻击者在用戶与服务器通讯过程中便难以拦截、篡改信息以及冒充身份变
    add_header Strict-Transport-Security max-age=31536000;

    # ...
}

然后再加一个server块,监听80端口,301跳转到https:

server {
    listen 80;
    server_name www.awaimai.com awaimai.com;
    add_header Strict-Transport-Security max-age=31536000;
    return 301 https://www.awaimai.com$request_uri;
}

重启服务即可。

5 使用赫尔曼密钥(可选)

为了更安全 ,可以考虑使用迪菲-赫尔曼密钥交换。

在Shell命令行:

$ cd /etc/nginx/ssl
$ openssl dhparam -out dhparam.pem 2048           # 生成迪菲-赫尔曼密钥

然后在nginx ssl配置的后面,加上下面的配置:

server {
    # ...

    # 设置服务器加密方式优先于客户端
    ssl_prefer_server_ciphers on;
    # 指定迪菲-赫尔曼密钥位置
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    # 使用安全协议,禁止其他不安全的 SSL 协议
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    # 禁止已经不安全的加密算法
    ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

    # ...
}

6 HTTPS服务器优化(可选)

SSL 的运行计算需要消耗额外的 CPU 资源,其中 SSL 通讯过程中『握手』阶段的运算最占用 CPU 资源。

有两个方法可以减少每台客户端的运算量:

  • 激活 keepalive 长连接,一个连接发送更多个请求
  •  配置ssl_session_cache,复用 SSL 会话参数,在并发的连接数中避免进行多次 SSL『握手』

这样,session会存储在一个 SSL 会话缓存里面。

1M 的ssl_session_cache大概包含 4000 个会话。

然后利用客戶端在『握手』阶段使用的 seesion id 去查询服务端的 session cache,简化『握手』阶段。

另外,还可以使用 ssl_session_timeout 配置缓存超时时间,默认5分钟。

配置范例:

server {
    # ...

    # 配置共享会话缓存大小
    ssl_session_cache shared:SSL:10m;
    # 配置会话超时时间
    ssl_session_timeout 10m;
    # 设置长连接
    keepalive_timeout 70;
    # ...
}

一般情況下,还可以加上以下几个命令,增强安全性:

# 减少点击劫持
add_header X-Frame-Options DENY;
# 禁止服务器自动解析资源类型
add_header X-Content-Type-Options nosniff;
# 防XSS攻击
add_header X-Xss-Protection 1;

7 配置 HTTP/2 (可选)

HTTP/2于2015年5月作为互联网标准正式发布。

它基于Google制定的SPDY协议,由IETF HTTPbis小组制定维护。

并且Google表示将放弃SPDY转而全力支持HTTP/2。

HTTP/2 与 HTTP/1.x 的主要区别:

  • 基于二进制而不是文本的
  • 完全多路复用,代替原来的排序和阻塞机制
  • 在一条连接中并行处理多个请求
  • 压缩头部减少开销
  • 允许服务器主动推送响应到客户端的缓存中

配置HTTP/2之前,我们要确保:

  • openssl版本为1.0.2+,因为HTTP/2需要openssl支持ALPN
  • Nginx版本为1.9.5+,因为这个版本默认编入了--with-http_v2_module
$ openssl version            # openssl必须为1.0.2或以上版本
$ nginx -V                   # 1.9.5或以上版本,低于这个版本需要自行编译加入--with-http_v2_module

如果版本不满足要求,则需要重新编译,编译之前需要安装一些依赖工具和库:

$ yum install gcc-c++ pcre-devel zlib-devel make wget openssl-devel libxml2-devel libxslt-devel gd-devel perl-ExtUtils-Embed GeoIP-devel gperftools-devel

7.1 更新openssl

openssl更新步骤:

$ wget https://www.openssl.org/source/openssl-1.0.2-latest.tar.gz
$ tar -zxf openssl-1.0.2-latest.tar.gz
$ cd openssl-1.0.2k
$ ./config
$ make depend
$ make
$ make install

完成之后,如果还是显示原来版本,则:

$ mv /usr/bin/openssl ~                                 # 备份原来的文件
$ ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl     # 新的bin文件软链到系统/usr/bin目录下

 7.2 重新编译nginx

用nginx -V命令可以看到nginx编入的 openssl 版本,我的是OpenSSL 1.0.1e-fips,明显不满足要求。

我们需要编入为openssl 1.0.2 或以上版本,所以要重新编译一遍nginx。

$ wget http://nginx.org/download/nginx-1.10.3.tar.gz
$ tar zxf nginx-1.10.3.tar.gz
$ cd nginx-1.10.3
$ ./configure ...
$ make
$ make install

./configure后面的配置项请自行加上,也可以直接拷贝nginx -V命令输出的配置参数。

7.3 配置http2

在端口监听处加上http2就可以了:

server {
    # ...

    listen 443 ssl http2;

    # ...
}

7 总结

最后嘛,为啥窝会想到弄成https呢?

因为窝知道很多路由器可以开启网站访问log,会记录下访问者IP、所访问的url、以及get/post参数内容。这表示什么吗?

这表示如果你通过这个路由器上网,然后在普通 http 网页上做登陆操作,那么路由器的管理员可以通过查 log 得到你对应网站的登陆信息。

这是非常危险的。

因为绝大多数的网站在传输登陆信息的时候,均使用明文发送密码,这样你的帐号密码就被看得一清二楚了。

但是 https 的话,路由器根本无法知道你所访问的 url 是什么,更别说具体的参数内容了,只能知道访问者的IP和目标IP,安全性自然大大提高。

比如你在麦当劳用免费 wifi,那么那边的路由管理员就知道你的帐号密码了。

或者电信联通什么的也会知道,因为它们在政策下必须保留至少3个月的路由数据,而你的帐号密码就在那里保存至少三个月。

想要抓你只要把你的登陆记录找到,然后登陆你的帐号,查你的个人信息就知道你到底是谁了。

也就是说,任何非https连接下做登陆或其它敏感操作是非常危险的,所有的操作被完整的记录了下来。

本文除了介绍SSL/TLS的配置外,还简要的介绍了一下其必要性,科普一下网络安全方面的东西。

如对这方面还有疑问,欢迎留言或mail窝。

 

参考资料:

  1. How To Create an SSL Certificate on Nginx for Ubuntu 14.04
  2. Nginx 配置 HTTPS 服务器
  3. Nginx 配置 SSL 证书 + 搭建 HTTPS 网站
  4. openssl 证书请求和自签名命令req详解
  5. 使用openssl生成sha256自签名证书
  6. 或许是 Nginx 上配置 HTTP2 最实在的教程了
  7. Open Source NGINX 1.9.5 Released with HTTP/2 Support
  8. How to upgrade OpenSSL in CentOS 6.5 / Linux / Unix from source?
  9. How to upgrade OpenSSL on Centos 7 or RHEL 7
  10. HTTP/2 一定要先升级到 HTTPS 么?
  11. 配置Nginx,开启HTTP/2
  12. How to get already installed NGINX to use OpenSSL 1.0.2 for ALPN?