TLS握手是HTTPS连接建立的第一道关卡,Nginx的ngx_http_ssl_module模块负责在这个阶段完成证书展示、密钥交换和加密协商。这个模块并非Nginx默认编译项,需要通过--with-http_ssl_module参数显式启用。模块提供的指令数量众多,从证书加载到会话复用,每一条指令都直接影响着连接的安全性和握手效率。理解这些指令的作用机制,是配置高性能HTTPS站点的基础。

基础证书与密钥配置指令
证书和私钥是HTTPS服务的身份凭证,ssl_certificate指令用于指定PEM格式的证书文件路径,而ssl_certificate_key指令则指向对应的私钥文件。这两个指令通常成对出现在server块中,是启用HTTPS的最小配置集合。证书文件中可以包含证书链,Nginx会按照从叶子证书到根证书的顺序发送给客户端,帮助客户端完成完整的信任链验证。如果证书链不完整,部分浏览器会报出证书不受信任的错误,而将中间证书拼接到证书文件末尾就能解决这个问题。
当私钥文件设置了密码保护时,Nginx启动过程会暂停并等待输入密码,这在自动化部署场景中显然不可行。ssl_password_file指令允许你指定一个文本文件,每行存放一个密码,Nginx会依次尝试这些密码来解密私钥。这种方式既保持了私钥的安全性,又避免了交互式输入的麻烦。需要注意的是,密码文件的权限应当严格限制为仅Nginx worker进程可读,防止密码泄露。
server {
listen 443 ssl;
server_name ipipp.com;
# 指定证书文件路径(包含证书链)
ssl_certificate /etc/nginx/ssl/ipipp.com_fullchain.pem;
# 指定私钥文件路径
ssl_certificate_key /etc/nginx/ssl/ipipp.com_privkey.pem;
# 指定私钥密码文件(如果私钥有密码保护)
ssl_password_file /etc/nginx/ssl/password.txt;
}从Nginx 1.11.0版本开始,ssl_certificate和ssl_certificate_key指令支持在同一server块中多次声明,用于配置多份证书。这个特性最典型的应用场景是同时部署RSA证书和ECDSA证书,Nginx会在TLS握手阶段根据客户端支持的算法自动选择合适的证书。这种双证书部署方式兼顾了兼容性和安全性,现代浏览器使用更高效的ECDSA证书,而老旧客户端则回退到RSA证书。
协议版本与加密套件控制
TLS协议经历了多个版本的演进,每个版本在安全性和性能上都有差异。ssl_protocols指令用于控制Nginx接受哪些TLS协议版本,默认值包含了TLSv1和TLSv1.1,但这两个版本已被发现存在安全漏洞,建议仅保留TLSv1.2和TLSv1.3。TLSv1.3作为最新标准,简化了握手流程,将往返次数从两次减少到一次,大幅降低了延迟,同时移除了所有不安全的加密算法。不过需要注意,TLSv1.3需要OpenSSL 1.1.1及以上版本的支持,旧版Nginx可能无法使用。
加密套件决定了握手过程中使用的密钥交换算法、认证算法、对称加密算法和消息认证码。ssl_ciphers指令接受一个加密套件列表,套件之间用冒号分隔,Nginx会按照列表顺序优先选择靠前的套件。配置加密套件是一个需要谨慎对待的工作,过于宽松会引入弱算法,过于严格则可能导致旧客户端无法连接。一个比较稳妥的做法是参考Mozilla的推荐配置,根据你的兼容性需求选择Intermediate或Modern级别。
server {
listen 443 ssl;
server_name ipipp.com;
# 仅启用TLS 1.2和TLS 1.3
ssl_protocols TLSv1.2 TLSv1.3;
# 配置加密套件(TLS 1.3的套件由ssl_conf_command控制)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# 优先使用服务端配置的加密套件顺序
ssl_prefer_server_ciphers on;
}ssl_prefer_server_ciphers指令控制加密套件的选择权归属。设置为on时,Nginx按照ssl_ciphers列表的顺序选择第一个客户端也支持的套件;设置为off时,则由客户端的偏好决定。从安全角度看,服务端优先更合理,因为服务端管理员比客户端更了解哪些套件是安全的。不过在TLSv1.3中,加密套件的选择逻辑发生了变化,这个指令对TLSv1.3连接不再生效,TLSv1.3的套件需要通过ssl_conf_command指令来配置。
会话缓存与性能优化指令
TLS握手是一个计算密集型操作,涉及多次非对称加密运算,每次连接都从头握手会消耗大量CPU资源。会话复用机制允许客户端在后续连接中引用之前的握手结果,跳过密钥交换阶段直接恢复加密通道。ssl_session_cache指令配置会话缓存存储方式,shared表示所有worker进程共享一块内存区域,后面跟名称和大小。大小为1MB的缓存大约可以存储4000个会话,根据站点并发量来调整这个值。除了shared,还可以使用builtin利用OpenSSL内置缓存,但这种方式每个worker进程独立缓存,效率较低,不推荐使用。
ssl_session_timeout指令设定会话缓存的有效期,默认值为5分钟。这个时间需要在安全性和性能之间权衡,过短会导致复用率低下,过长则增加会话被劫持的风险。对于访问频繁的站点,适当延长到10到30分钟可以显著降低CPU负载。会话票据是另一种复用机制,通过ssl_session_tickets指令控制,它使用加密票据将会话状态存储在客户端,减轻服务端内存压力。不过会话票据的密钥需要定期轮换,否则长期使用同一密钥会带来安全隐患。
http {
# 配置共享会话缓存,大小为10MB,约可存储40000个会话
ssl_session_cache shared:SSL:10m;
# 会话缓存有效期为10分钟
ssl_session_timeout 10m;
# 启用会话票据
ssl_session_tickets on;
# 配置会话票据密钥文件(包含48字节的随机数据)
ssl_session_ticket_key /etc/nginx/ssl/ticket.key;
server {
listen 443 ssl;
server_name ipipp.com;
ssl_certificate /etc/nginx/ssl/ipipp.com_fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/ipipp.com_privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
}
}OCSP装订是另一个值得关注的优化点。当浏览器验证证书时,通常需要额外请求OCSP服务器确认证书是否被吊销,这个额外请求会增加页面加载延迟。ssl_stapling指令启用OCSP装订功能后,Nginx会主动获取OCSP响应并缓存在本地,握手时直接将响应发送给客户端,省去了客户端的额外请求。启用这个功能需要配置ssl_trusted_certificate指令指向包含根证书和中间证书的文件,否则Nginx无法验证OCSP响应的签名。同时建议设置resolver指令,确保Nginx能够解析OCSP服务器的域名。
安全加固与高级配置
Diffie-Hellman密钥交换算法的安全性依赖于一个足够大的素数参数,默认情况下Nginx使用OpenSSL内置的1024位DH参数,这在当前计算能力下已经不够安全。ssl_dhparam指令允许你指定自定义的DH参数文件,建议使用2048位甚至更高位数的参数。生成DH参数文件可以使用OpenSSL命令openssl dhparam -out dhparam.pem 2048,这个过程比较耗时,因为需要寻找大素数,但这是一次性工作。使用更强的DH参数可以有效防御Logjam攻击等安全威胁。
双向TLS认证是一种比单向认证更严格的安全机制,不仅服务端向客户端证明身份,客户端也需要出示证书供服务端验证。ssl_verify_client指令控制客户端证书验证行为,设置为on表示强制要求客户端出示有效证书,optional表示接受但非强制,off表示不验证。ssl_verify_depth指令设定证书链验证的最大深度,ssl_client_certificate指令指向受信任的CA证书文件。这种配置常用于内部API服务,确保只有持有特定证书的客户端才能访问。
server {
listen 443 ssl;
server_name api.ipipp.com;
ssl_certificate /etc/nginx/ssl/api_fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/api_privkey.pem;
# 自定义DH参数
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
# 强制客户端证书验证
ssl_verify_client on;
# 证书链验证深度为2
ssl_verify_depth 2;
# 受信任的CA证书
ssl_client_certificate /etc/nginx/ssl/ca.crt;
# 仅允许验证通过的请求访问
location / {
if ($ssl_client_verify != SUCCESS) {
return 403;
}
proxy_pass http://backend;
}
}HSTS(HTTP严格传输安全)虽然不是ngx_http_ssl_module的指令,但它是HTTPS安全配置中不可或缺的一环。通过add_header指令向响应添加Strict-Transport-Security头,告诉浏览器在指定时间内只通过HTTPS访问该站点。配置max-age=31536000表示一年期内强制使用HTTPS,includeSubDomains将规则扩展到子域名,preload允许站点被加入浏览器的HSTS预加载列表。在启用HSTS之前,要确保站点已经全面支持HTTPS且所有资源都通过HTTPS加载,否则可能导致部分页面无法访问。