概述
本章,您将学习到关于 https 的相关知识。
前面在 Apache httpd 中介绍过三部分的内容:
- 第一部分 - https 的基础知识
- 第二部分 - 手动模拟出 SSL 证书
- 第三部分 - 在 Apache httpd 中进行配置
本章只说明如何在 Nginx 中配置 https 的相关内容。
实际情况下的 SSL 证书
在实际的生产情况下,当我们向证书品牌商购买 SSL 证书后,可在网页的控制台中手动下载相关文件,包括:
- 私钥文件 - 常以 .key 后缀结尾的文件
- 证书文件 - 常以 .crt 后缀结尾的文件
- 证书链文件 - 常以 .crt 后缀结尾的文件,文件名中常包含 chain 或 root 等单词
Q:为什么还要有一个证书链文件?
CA 机构通常不会直接使用根证书的私钥对用户的证书进行签发,这是因为:
- 安全风险极高 - 如果把根证书的私钥拿出来每天给成千上万的用户签名,若根证书私钥泄露,所有签发的证书全部作废,整个信任体系直接崩塌
- 难以维护 - 根证书的变更或轮换或吊销需要全球所有的操作系统和浏览器更新,成本巨大
为了解决上面的问题,CA 机构采用分层签发模式,即 "根证书" -> "中间证书" -> "用户的签发证书" 这样的层级结构。根证书的私钥被离线存储在最高安全级别的硬件加密模块中,极少使用。
浏览器仅内置根证书,并不预置中间证书,因此在 Web 服务器中必须配置证书链(中间证书),用以向浏览器展示完整的信任路径(Trust Path),即 "根证书" <---> "中间证书" <---> "用户的签发证书"。若 Web 服务器缺少证书链的配置,将导致浏览器因无完整的信任链而出现拒绝连接的情况。
相关指令
证书以及私钥相关的指令:
-
ssl_certificate指令- 指令参数为一个 PEM 格式的完整证书链文件,该文件内容必须按照以下顺序包含:- 证书文件的内容 - CA 机构给你域名签发的文件
- 证书链文件的内容
可以使用
cat命令进行内容合并,如:Shell > cat your_domain.crt root_bundle.crt > fullchain.pem -
ssl_certificate_key指令 - 指定签发证书对应的私钥文件
安全相关指令:
-
ssl_protocols指令 - 启用特定的协议,语法为ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3];,默认为ssl_protocols TLSv1.2 TLSv1.3; -
ssl_ciphers指令 - 在 SSL 握手期间选择协商的加密套件,加密套件用英语冒号进行分隔,优先级按照从左到右排列,默认为ssl_ciphers HIGH:!aNULL:!MD5;。可配置的加密套件可通过命令openssl ciphers进行查询Shell > openssl ciphers -
ssl_prefer_server_ciphers指令 - 指定在使用 SSLv3 和 TLS 协议时,是否优先使用服务器加密套件而非客户端加密套件
性能优化相关指令:
-
ssl_session_cache- 存储之前建立过的 SSL/TLS 握手会话信息,使后续的客户端可以复用这些连接会话参数,跳过耗时的完整握手,减少耗时并提高并发性能。语法为ssl_session_cache off | none | [builtin[:size]] [shared:name:size];,示例配置ssl_session_cache shared:SSL:10m;表示所有 worker 进程共享的缓存,在实际情况下可存储约 20000 - 30000 个会话( 1 兆字节大约可存储 2000 - 3000 个会话) -
ssl_session_timeout- 服务器端 "记住" 某个 SSL/TLS 握手会话的最长时间,通常都设置为 10 分钟或 15 分钟 -
ssl_stapling指令 - 启用或禁用 OCSP Stapling(OCSP 装订)功能。- 传统模式 - 当浏览器在 SSL/TLS 握手后,需要单独向 CA 机构的 OCSP 服务器发起请求,查询证书是否被吊销。这会增加额外的网络延迟(通常 150-300ms),且暴露用户访问隐私
- 装订模式 - Nginx 服务器会定期主动向 CA 的 OCSP 服务器查询证书状态,并将获取到的、带有时间戳和签名的 OCSP 响应 "装订" 在 TLS 握手的 ServerHello 消息中发送给客户端,降低 https 的加载延迟,提升用户体验
-
ssl_stapling_verify指令 - 是否对从 CA 获取的 OCSP 响应进行签名验证 -
resolver指令 - 配置 Nginx 内部使用的 DNS,主要是配合 OCSP Stapling -
resolver_timeout指令 - 设置 Nginx 进行单次 DNS 查询的超时等待时间上限,通常设置为 3s 或 5s
生产环境中比较标准的 https 示例配置:
user nginx;
worker_processes 4;
error_log logs/error.log error;
pid logs/nginx.pid;
worker_rlimit_nofile 7400;
events {
use epoll;
worker_connections 1024;
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
tcp_nopush on;
# 长连接的超时时间
keepalive_timeout 30;
# 隐藏响应头的版本信息
server_tokens off;
# 压缩响应相关
gzip on;
gzip_vary on;
gzip_buffers 32 4K;
gzip_comp_level 5;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml application/xhtml+xml image/svg+xml application/x-font-ttf application/x-font-opentype application/vnd.ms-fontobject font/woff;
gzip_min_length 256;
# 80 端口的 server 上下文
server {
listen 192.168.100.20:80;
server_name www.my-tests.com;
return 301 https://www.my-tests.com$request_uri;
}
# 443 端口的 server 上下文
server {
# 443 端口的 server 上下文中,listen 指令的指令参数通常都有 ssl 和 http2
listen 192.168.100.20:443 ssl http2 backlog=8000 reuseport;
server_name www.my-tests.com;
ssl_certificate /usr/local/nginx/ssl/fullchain.pem;
ssl_certificate_key /usr/local/nginx/ssl/my_privkey.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
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_session_cache shared:SSL:10m;
ssl_session_timeout 15m;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
...
location / {
...
}
...
}
}
此处的 https 示例配置不能包含所有的业务使用场景,使用者应该根据实际情况酌情增加或减少相关的配置。
校验加密套件
校验主配置文件 nginx.conf 中的加密套件:
Shell > openssl ciphers -v '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'
TLS_AES_256_GCM_SHA384 TLSv1.3 Kx=any Au=any Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any Au=any Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256 TLSv1.3 Kx=any Au=any Enc=AESGCM(128) Mac=AEAD
TLS_AES_128_CCM_SHA256 TLSv1.3 Kx=any Au=any Enc=AESCCM(128) Mac=AEAD
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH Au=ECDSA Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH Au=RSA Enc=CHACHA20/POLY1305(256) Mac=AEAD
DHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(128) Mac=AEAD
DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(256) Mac=AEAD
输出中的每一行代表一个套件,其由以下信息组成:
- 套件名称 - 如 TLS_AES_256_GCM_SHA384,你在 nginx.conf 中配置的就使用这个套件名称
- SSL/TLS 版本 - 套件支持的最低 TLS 版本
- 密钥交换(Kx) - 客户端与服务器协商会话密钥所用的算法
- 身份认证(Au) - 签发证书用于身份验证的算法
- 对称加密(Enc) - 实际数据传输时的对称加密算法及密钥长度
- 消息摘要(Mac) - 完整性校验算法
在前面的 Apache httpd 中介绍过 https 的底层原理 —— https 的底层大量使用了非对称加密 + 对称加密 + 哈希算法。
-
非对称加密
证书的签名就是利用非对称加密实现的,另外在握手阶段,会用非对称加密完成预主密钥的 "协商" 或其他密钥材料的 "协商",并 "派生" 出会话密钥。
-
对称加密
握手完成后,后续所有通信数据都用对称加密传输,常见算法有 AES-128、AES-256、ChaCha20 等。
-
哈希算法
主要用来验证数据的完整性,防止数据被篡改。另外,证书的签名验证也依赖哈希算法。
┌─────────────┐
│ 握手阶段 │ 非对称加密(交换密钥)+ 哈希(验证身份)
├─────────────┤
│ 通信阶段 │ 对称加密(加密数据)+ 哈希/HMAC(防篡改)
└─────────────┘










