主题
🔒 HTTPS 与 SSL/TLS
HTTP 是明文传输,中间任何人都能看到你和服务器之间的对话内容。HTTPS 给 HTTP 加了一层 TLS 加密,让你的数据在传输中不可被窃听和篡改。
HTTPS 握手过程
HTTPS 的核心是 TLS 握手,整个过程在应用数据发送之前完成,大概分为四步:
客户端 服务器
│ │
│──── ClientHello ───────────────────────→│
│ (支持的加密套件,TLS版本,随机数) │
│ │
│←─── ServerHello + 证书 ────────────────│
│ (选定加密套件,服务器随机数,证书) │
│ │
│──── 密钥交换 + Finished ───────────────→│
│ (用证书公钥加密的预主密钥) │
│ │
│←─── Finished ──────────────────────────│
│ │
│══════ 对称加密通信开始 ══════════════════│关键点:
- 非对称加密只用于握手阶段安全交换"会话密钥"
- 后续数据传输用对称加密(AES-GCM 等),比非对称快得多
- 证书的作用是证明"你连接的这个服务器确实是它声称的那个"
Let's Encrypt + Certbot 免费证书
Let's Encrypt 提供免费且自动化的 SSL 证书,3 个月有效期,配合 Certbot 可自动续期。
bash
# Ubuntu/Debian 安装 certbot
sudo apt update
sudo apt install certbot python3-certbot-nginx
# 一键获取证书并自动配置 Nginx
sudo certbot --nginx -d example.com -d www.example.com
# 仅获取证书(不自动配 Nginx)
sudo certbot certonly --webroot -w /var/www/html -d example.com
# 测试续期是否正常(dry-run,不会产生实际续期请求)
sudo certbot renew --dry-run
# 手动续期
sudo certbot renew证书存放路径
Let's Encrypt 证书默认保存在 /etc/letsencrypt/live/your-domain/:
fullchain.pem— 完整证书链(证书 + 中间证书),Nginx 应使用此文件privkey.pem— 私钥,权限应为 0400 且仅 root 可读chain.pem— 中间证书(已包含在 fullchain.pem 中)
OpenSSL 自签证书(开发/内网用)
bash
# 生成 RSA 私钥(2048 位)
openssl genrsa -out server.key 2048
# 生成自签名证书(有效期 365 天)
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Dev/CN=localhost"
# 推荐的现代方式:Ed25519 私钥 + ECDSA 证书
openssl genpkey -algorithm ed25519 -out server.key
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=localhost"注意
自签证书浏览器会报"不安全",仅适用于开发环境。生产环境必须用 CA 签发证书。
HSTS:强制 HTTPS
HSTS(HTTP Strict Transport Security)告诉浏览器:这个域名以后只能用 HTTPS 访问,不许走 HTTP。
nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;max-age=63072000:2 年有效期includeSubDomains:子域名也强制 HTTPSpreload:可提交到浏览器的 HSTS Preload 列表
注意
一旦启用 HSTS 且被浏览器记住,再想切回 HTTP 就非常困难。确认 HTTPS 完全配置好后再加。
Nginx 完整 SSL 配置模板
nginx
server {
listen 80;
server_name example.com www.example.com;
# 所有 HTTP 请求 301 跳转到 HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on; # Nginx ≥ 1.25.1 需单独写 http2 on(旧版可写 listen 443 ssl http2)
server_name example.com www.example.com;
# 证书路径
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL 协议 — 仅启用 TLSv1.2+
ssl_protocols TLSv1.2 TLSv1.3;
# 加密套件 — Mozilla SSL Configuration Generator Intermediate 级别
# 兼容主流浏览器(Firefox 27+、Chrome 31+、IE 11+、Safari 9+)
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 off; # TLSv1.3 无视此设置;TLSv1.2 下 off 让客户端选择最佳套件
# DH 参数(需预先用 openssl dhparam 生成)
ssl_dhparam /etc/nginx/dhparam.pem;
# 安全头
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header Content-Security-Policy "default-src 'self'" always;
# OCSP Stapling — 减轻 OCSP Responder 压力并提升 TLS 握手速度
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# DNS 解析器(Google Public DNS,可按需替换为内网 DNS)
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
root /var/www/html;
index index.html;
}生成 DH 参数(增强安全性)
bash
# 4096 位 DH 参数,安全性更高;生成时间约 3-10 分钟,请耐心等待
openssl dhparam -out /etc/nginx/dhparam.pem 4096SSL Labs 评分冲 A+
SSL Labs 是最权威的 SSL 配置评分工具,满分 A+。
| 评分项 | 达标条件 |
|---|---|
| 证书 | 有效、信任链完整、签名算法安全 |
| 协议 | 关闭 TLSv1.0/1.1,启用 TLSv1.2/1.3 |
| 加密套件 | 只保留前向安全的 AEAD 套件 |
| HSTS | 配置严格传输安全头(includeSubDomains + preload) |
| 密钥交换 | RSA ≥ 2048,或 ECDSA |
| 重协商 | 安全重协商或无重协商 |
常见坑
证书过期忘了续
Let's Encrypt 证书有效期只有 90 天。设置自动续期 + 告警:
bash
# certbot 自动续期(会自行注册 systemd timer)
sudo systemctl status certbot.timer
# 添加告警脚本到 cron
# 每天检查证书是否需要续期,--quiet 静默运行
0 8 * * * certbot renew --quiet --post-hook "systemctl reload nginx"中间证书缺失
使用 fullchain.pem(而非仅 cert.pem)可以确保中间证书也被发送。中间证书缺失会导致部分客户端(如老版本 Android)无法建立连接。同时应配置 ssl_trusted_certificate 指向 chain.pem 以确保 OCSP Stapling 正常工作。
泛域名证书的局限
Let's Encrypt 泛域名证书(*.example.com)只有用 DNS 验证才能签发,HTTP 验证不支持泛域名。而且 example.com 和 *.example.com 是两个独立的证书域名,申请时要一起写上。
🧪 练习题
- 用 Certbot 为自己域名申请 Let's Encrypt 证书并配置 Nginx HTTPS
- 用 OpenSSL 生成一个内网用的自签名证书
- 将你的 Nginx SSL 配置提交到 SSL Labs 测试,尝试达到 A+ 评级
传输加密搞定了,接下来是服务端的身份认证安全——用户认证安全。
加载练习题中...