1 设置HTTPS服务器
为了设置HTTPS服务器,在你的nginx.conf文件中为server块中的listen指令指定ssl参数,然后设置服务器证书和秘钥文件位置:
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
}
服务器证书是公开的实体。它发送给每个连接服务器的客户端。秘钥是安全的实体并且应该存储在限制访问的文件中。然而,Nginx的主进程必须能够读取该文件。秘钥和证书存储在相同的文件中:
ssl_certificate www.example.com.cert;
ssl_certificate_key www.example.com.cert;
在这种情况下,文件访问权限也受到控制。虽然证书和秘钥存储在相同的文件中,只有证书发送给客户端。
ssl_protocols和ssl_ciphers指令能用于限制连接只包括强版本和SSL/TLS密码。
从1.0.5开始,Nginx默认使用ssl_protocols SSLv3 TLSv1和ssl_ciphers HIGH:!aNULL:!MD5;从1.1.13和1.0.12开始,默认更新为ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2。
有时会在老的密码设计中找到漏洞,你最好在现代Nginx中禁用这些配置(可惜,默认配置不易改变,因为要向后兼容)。请注意CBC模式密码可能容易受到攻击,尤其是BEAST,SSLv3最好避免除非你需要支持遗留客户端为了POODLE攻击。
2 HTTPS服务器优化
SSL操作消耗额外的CPU资源。大多数CPU密集操作是SSL握手。有两种方式可以减少每个客户端的这些操作的数量:
-
启用keepalive连接通过一个连接发送多个请求
-
重用SSL回话参数避免并行和后续连接的SSL握手
会还存储在SSL回话缓存中,在worker进程之间共享,通过ssl_session_cache指令配置。一兆字节缓存包含大约4000个回话。默认是5分钟超时。可以使用ssl_session_timeout指令调整超时时间。下面的配置是使用10兆字节共享回话缓存优化多核系统:
worker_processes auto;
http {
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
server {
listen 443 ssl;
server_name www.example.com;
keepalive_timeout 70;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
}
}
3 SSL证书链
有些浏览器可能需要证书是由著名的证书认证机构颁发,然而其它浏览器可能接收证书没有问题。这是因为发布证书的服务器使用中间证书在特定的浏览器中不是权威的证书认证机构。在这种情况下,证书提供证书链束应该连接到签名服务器证书。服务器证书必须出现在合并后的文件证书链之前:
$ cat www.example.com.crt bundle.crt > www.example.com.chained.crt
导致文件应该使用ssl_certificate指令:
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.chained.crt;
ssl_certificate_key www.example.com.key;
...
}
如果服务器证书和束连接顺序错误,Nginx将启动失败并显示以下错误消息:
SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed
(SSL: error:0B080074:x509 certificate routines:
X509_check_private_key:key values mismatch)
错误发生,因为Nginx尝试使用秘钥和束中的第一个证书替代服务器证书。
浏览器通常存储它们接收到的授权机构颁发的中间证书。为了确保服务器发送完整的证书链可以使用openssl命令行工具:
$ openssl s_client -connect www.godaddy.com:443
...
Certificate chain
0 s:/C=US/ST=Arizona/L=Scottsdale/1.3.6.1.4.1.311.60.2.1.3=US
/1.3.6.1.4.1.311.60.2.1.2=AZ/O=GoDaddy.com, Inc
/OU=MIS Department/CN=www.GoDaddy.com
/serialNumber=0796928-7/2.5.4.15=V1.0, Clause 5.(b)
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
/OU=http://certificates.godaddy.com/repository
/CN=Go Daddy Secure Certification Authority
/serialNumber=07969287
1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
/OU=http://certificates.godaddy.com/repository
/CN=Go Daddy Secure Certification Authority
/serialNumber=07969287
i:/C=US/O=The Go Daddy Group, Inc.
/OU=Go Daddy Class 2 Certification Authority
2 s:/C=US/O=The Go Daddy Group, Inc.
/OU=Go Daddy Class 2 Certification Authority
i:/L=ValiCert Validation Network/O=ValiCert, Inc.
/OU=ValiCert Class 2 Policy Validation Authority
/CN=http://www.valicert.com//emailAddress=info@valicert.com
...
In this example the subject (“s”) of the www.GoDaddy.com server certificate #0 is signed by an issuer (“i”) which itself is the subject of the certificate #1. This certificate #1 is signed by an issuer which itself is the subject of the certificate #2. This certificate, however, is signed by the well-known issuer ValiCert, Inc. whose certificate is stored in the browsers themselves.
If a certificate bundle has not been added, only the server certificate #0 will be shown.
4 单个HTTP/HTTPS服务器
可以配置单个服务器处理HTTP和HTTPS请求,使用一个带有ssl参数和一个没有ssl参数的listen在相同的虚拟主机中:
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
...
}
在Nginx0.7.13和更早版本中,SSL不能选择性的启用监听套接字。只能使用ssl指令启用整个服务器的SSL,让它不能简单一个HTTP/HTTPS服务器。Listen指令的ssl参数解决了这个问题。
5 基于名称的HTTPS服务器
一个常见的问题出现了,当两个或多个HTTPS服务器配置监听一个IP地址时:
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
...
}
server {
listen 443 ssl;
server_name www.example.org;
ssl_certificate www.example.org.crt;
...
}
使用该配置,浏览器接收到默认的服务器证书。在这种情况下,它是www.example.com不管请求的服务器的名称。这是SSL协议本身导致。浏览器发送HTTP请求前SSL连接已经建立,Nginx不知道请求的服务器名称。因此,它可能只提供默认的服务器证书。
最好的方式是分配不同的IP地址给每个HTTPS服务器:
server {
listen 192.168.1.1:443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
...
}
server {
listen 192.168.1.2:443 ssl;
server_name www.example.org;
ssl_certificate www.example.org.crt;
...
}
5.1 一个SSL证书使用多个名称
有其它方式在多个HTTPS服务器间共享单个IP地址。然而,它们都有自己的缺点。第一种方式使用SubjectAltName证书字段带有多个名字的证书,例如,www.example.com和www.example.org。然而,SubjectAltName字段长度是有限制的。
另一种方式是使用通配符名称,例如*.example.org。通配符证书在特定预的所有子域名中安全,但只在一个级别。该证书匹配www.example.org,但不匹配example.org或www.sub.example.org。这两种方式可以联合。可以包含精确和通配符名称在SubjectAltName字段。例如,example.org和*.example.org。
最好使用多个名称放置一个证书文件,它的秘钥在http级别配置,以至于它们继承单个内存副本给所有服务器:
ssl_certificate common.crt;
ssl_certificate_key common.key;
server {
listen 443 ssl;
server_name www.example.com;
...
}
server {
listen 443 ssl;
server_name www.example.org;
...
}
5.2 服务器名称指示
A more generic solution for running several HTTPS servers on a single IP address is the TLS Server Name Indication extension (SNI, RFC 6066), which allows a browser to pass a requested server name during the SSL handshake. With this solution, the server will know which certificate it should use for the connection. However, SNI has limited browser support. Currently it is supported starting with the following browser versions:
Opera 8.0;
MSIE 7.0 (but only on Windows Vista or higher);
Firefox 2.0 and other browsers using Mozilla Platform rv:1.8.1;
Safari 3.2.1 (Windows version supports SNI on Vista or higher);
and Chrome (Windows version supports SNI on Vista or higher, too).
Only domain names can be passed in SNI. However, some browsers will pass the IP address of the server as its name if a request includes a literal IP address. It is best not to rely on this.
In order to use SNI in nginx, it must be supported in both the OpenSSL library with which the NGINX binary has been built with as well as the library which it is being dynamically linked with at run time. OpenSSL supports SNI since the 0.9.8f version if it was built with configuration option --enable-tlsext. Since OpenSSL version 0.9.8j, this option is enabled by default. If NGINX was built with SNI support, NGINX shows the following when run with the -V switch:
$ nginx -V
...
TLS SNI support enabled
...
However, if the SNI-enabled NGINX is linked dynamically to an OpenSSL library without SNI support, NGINX displays the warning:
NGINX was built with SNI support, however, now it is linked
dynamically to an OpenSSL library which has no tlsext support,
therefore SNI is not available