SSL/TLS 与 HTTPS 配置
大约 13 分钟约 3955 字
SSL/TLS 与 HTTPS 配置
简介
SSL/TLS 协议为网络通信提供加密和身份验证,HTTPS 则是 HTTP over TLS 的安全传输方式。正确配置 SSL/TLS 涉及证书类型选择、证书申请与部署、Web 服务器加密配置以及证书自动续期等多个环节。掌握这些知识对于保障 Web 应用的数据传输安全至关重要。
特点
证书类型
证书分类对比
| 分类 | 说明 | 适用场景 |
|---|---|---|
| DV(域名验证) | 仅验证域名所有权,签发快 | 个人网站、测试环境 |
| OV(组织验证) | 验证域名和组织信息 | 企业网站、电商 |
| EV(扩展验证) | 最严格的身份验证,浏览器显示公司名 | 金融、支付类网站 |
| 通配符证书 | 覆盖主域名及所有子域名 | 多子域名场景 |
| 自签名证书 | 自行签发,无 CA 信任 | 内部开发测试 |
证书文件格式
| 格式 | 扩展名 | 说明 |
|---|---|---|
| PEM | .pem, .crt, .key | Base64 编码,Nginx/Apache 常用 |
| DER | .der, .cer | 二进制格式,Java 常用 |
| PKCS#7 | .p7b, .p7c | 包含证书链,不含私钥 |
| PKCS#12 | .pfx, .p12 | 包含证书和私钥,IIS 常用 |
# 格式转换
# PEM 转 DER
openssl x509 -in cert.pem -outform DER -out cert.der
# DER 转 PEM
openssl x509 -in cert.der -inform DER -outform PEM -out cert.pem
# PEM 转 PKCS#12
openssl pkcs12 -export -in cert.pem -inkey key.pem -out cert.pfx
# PKCS#12 转 PEM
openssl pkcs12 -in cert.pfx -out cert.pem -nodes
# 查看证书信息
openssl x509 -in cert.pem -text -noout
# 查看证书有效期
openssl x509 -in cert.pem -noout -dates
# 验证证书链
openssl verify -CAfile ca-bundle.crt cert.pem
# 检查私钥与证书是否匹配
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in key.pem | openssl md5Let's Encrypt 免费证书
使用 Certbot 申请证书
# 安装 Certbot
# Ubuntu/Debian
apt install -y certbot python3-certbot-nginx
# CentOS/RHEL
yum install -y certbot python3-certbot-nginx
# 使用 Nginx 插件自动申请并配置
certbot --nginx -d example.com -d www.example.com
# 仅申请证书(手动配置 Nginx)
certbot certonly --webroot \
-w /var/www/html \
-d example.com \
-d www.example.com \
--email admin@example.com \
--agree-tos \
--non-interactive
# 申请通配符证书(需要 DNS 验证)
certbot certonly --manual \
--preferred-challenges dns \
-d "*.example.com" \
-d example.com \
--email admin@example.com \
--agree-tos
# 查看已申请的证书
certbot certificates
# 手动续期测试(不实际续期)
certbot renew --dry-run
# 手动续期
certbot renew
# 证书文件位置
# 证书: /etc/letsencrypt/live/example.com/fullchain.pem
# 私钥: /etc/letsencrypt/live/example.com/privkey.pem
# 证书链: /etc/letsencrypt/live/example.com/chain.pem使用 acme.sh 申请证书
# 安装 acme.sh
curl https://get.acme.sh | sh -s email=admin@example.com
# 使用 Webroot 模式申请
acme.sh --issue -d example.com -d www.example.com --webroot /var/www/html
# 使用 Nginx 模式申请
acme.sh --issue -d example.com --nginx
# 使用 DNS API 申请通配符证书(以 Cloudflare 为例)
export CF_Key="your_api_key"
export CF_Email="admin@example.com"
acme.sh --issue --dns dns_cf -d "*.example.com" -d example.com
# 安装证书到指定目录
acme.sh --install-cert -d example.com \
--key-file /etc/ssl/private/example.com.key \
--fullchain-file /etc/ssl/certs/example.com.crt \
--reloadcmd "systemctl reload nginx"
# 查看已安装的证书
acme.sh --list
# 强制续期
acme.sh --renew -d example.com --force
# 自动续期(acme.sh 安装后自动创建 cron 任务)
# 可通过 crontab -l 查看Nginx SSL 配置
推荐的 HTTPS 配置
# /etc/nginx/conf.d/ssl-hardened.conf
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name example.com www.example.com;
# Let's Encrypt 验证路径(必须在重定向之前)
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS 服务器配置
server {
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;
# Mozilla 推荐的 SSL 配置 (Intermediate)
ssl_protocols TLSv1.2 TLSv1.3;
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;
# SSL 会话优化
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# DH 参数(增强密钥交换安全性)
# 生成: openssl dhparam -out /etc/ssl/dhparam.pem 2048
ssl_dhparam /etc/ssl/dhparam.pem;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 安全响应头
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
# 站点配置
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}SSL 安全等级配置
| 等级 | TLS 版本 | 适用场景 |
|---|---|---|
| Modern | TLSv1.3 | 仅需支持最新浏览器 |
| Intermediate | TLSv1.2, TLSv1.3 | 通用 Web 服务(推荐) |
| Old | TLSv1.0, TLSv1.1, TLSv1.2, TLSv1.3 | 兼容老旧客户端 |
# Modern 配置(仅 TLS 1.3)
# ssl_protocols TLSv1.3;
# ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
# ssl_prefer_server_ciphers off;
# Intermediate 配置(推荐)
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:...;
# ssl_prefer_server_ciphers off;多域名证书配置
# 多个域名使用不同证书
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
location / {
proxy_pass http://127.0.0.1:3000;
}
}Nginx SSL 配置
安全响应头详解
# HTTP 安全响应头配置详解
# HSTS — 强制浏览器使用 HTTPS
# max-age: 浏览器记住使用 HTTPS 的时间(秒)
# includeSubDomains: 包含所有子域名
# preload: 允许加入浏览器的 HSTS 预加载列表
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# X-Frame-Options — 防止点击劫持
# DENY: 完全禁止嵌入 iframe
# SAMEORIGIN: 只允许同源页面嵌入
add_header X-Frame-Options SAMEORIGIN always;
# X-Content-Type-Options — 防止 MIME 类型嗅探
# nosniff: 强制浏览器使用服务器声明的 Content-Type
add_header X-Content-Type-Options nosniff always;
# X-XSS-Protection — 浏览器 XSS 过滤(已被 CSP 替代)
add_header X-XSS-Protection "1; mode=block" always;
# Content-Security-Policy — 内容安全策略
# 限制页面可以加载的资源来源
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;
# Referrer-Policy — 控制 Referer 头的发送
# strict-origin-when-cross-origin: 跨域请求只发送来源域名
add_header Referrer-Policy strict-origin-when-cross-origin always;
# Permissions-Policy — 控制浏览器功能的使用权限
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# Cross-Origin 相关头
add_header Cross-Origin-Embedder-Policy require-corp always;
add_header Cross-Origin-Opener-Policy same-origin always;
add_header Cross-Origin-Resource-Policy same-origin always;SSL 性能优化
# SSL 性能优化配置
server {
listen 443 ssl http2;
server_name example.com;
# 1. TLS 1.3 0-RTT(零往返时间恢复)
# 首次连接后,后续连接可以跳过握手
ssl_early_data on;
# 2. OCSP Stapling — 减少客户端验证延迟
# 客户端不需要单独查询 OCSP 服务器
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 3. 会话缓存 — 复用 TLS 会话参数
# shared: 所有 worker 进程共享
# 10m ≈ 40000 个会话
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# 4. 会话票据 — 无状态的会话恢复
ssl_session_tickets on;
# 手动配置票据密钥(重启后仍可恢复)
ssl_session_ticket_key /etc/ssl/ticket.key;
# 5. 减少证书链大小
# 只包含必要的中间证书
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
# 6. 启用 HTTP/2
# HTTP/2 多路复用减少连接数
# ALPN 协议协商比 NPN 更快
listen 443 ssl http2;
# 7. 开启 gzip(减少传输量)
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1024;
}
# 性能对比:
# 无优化: TLS 握手 ~200ms + 数据传输
# 会话复用: ~0ms(跳过握手)
# TLS 1.3: ~100ms(1-RTT),恢复 0-RTT
# OCSP Stapling: 省去客户端 OCSP 查询 ~50msIIS SSL 配置
# IIS SSL 配置(Windows Server)
# 1. 导入证书
Import-Module WebAdministration
$certPath = "C:\certs\example.com.pfx"
$certPassword = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
Import-PfxCertificate -FilePath $certPath -CertStoreLocation Cert:\LocalMachine\My -Password $certPassword
# 2. 绑定证书到网站
$siteName = "Default Web Site"
$binding = Get-WebBinding -Name $siteName -Protocol "https"
if ($binding) {
Remove-WebBinding -Name $siteName -Protocol "https"
}
New-WebBinding -Name $siteName -Protocol "https" -Port 443 -IPAddress "*"
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -like "*example.com*" } | Select-Object -First 1
$binding = Get-WebBinding -Name $siteName -Protocol "https"
$binding.AddSslCertificate($cert.Thumbprint, "My")
# 3. 配置 HTTPS 重定向
# 安装 URL Rewrite 模块后添加 web.config 规则
$webConfig = @"
<system.webServer>
<rewrite>
<rules>
<rule name="HTTP to HTTPS redirect" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="off" ignoreCase="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
</system.webServer>
"@
# 4. 配置 TLS 协议版本(注册表)
# 仅启用 TLS 1.2 和 TLS 1.3
$tlsPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols"
# 禁用 TLS 1.0
New-Item -Path "$tlsPath\TLS 1.0\Server" -Force
Set-ItemProperty -Path "$tlsPath\TLS 1.0\Server" -Name "Enabled" -Value 0
Set-ItemProperty -Path "$tlsPath\TLS 1.0\Server" -Name "DisabledByDefault" -Value 1
# 禁用 TLS 1.1
New-Item -Path "$tlsPath\TLS 1.1\Server" -Force
Set-ItemProperty -Path "$tls_path\TLS 1.1\Server" -Name "Enabled" -Value 0
Set-ItemProperty -Path "$tlsPath\TLS 1.1\Server" -Name "DisabledByDefault" -Value 1
# 启用 TLS 1.2
New-Item -Path "$tlsPath\TLS 1.2\Server" -Force
Set-ItemProperty -Path "$tlsPath\TLS 1.2\Server" -Name "Enabled" -Value 1
Set-ItemProperty -Path "$tlsPath\TLS 1.2\Server" -Name "DisabledByDefault" -Value 0证书自动续期
Certbot 自动续期
# Certbot 安装时已自动创建 systemd timer 或 cron 任务
# 查看定时任务
systemctl list-timers | grep certbot
# 或
crontab -l | grep certbot
# /etc/cron.d/certbot 内容示例
# 0 */12 * * * root certbot renew --quiet --post-hook "systemctl reload nginx"
# 自定义续期钩子
# /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
#!/bin/bash
systemctl reload nginx
# 续期前钩子
# /etc/letsencrypt/renewal-hooks/pre/backup.sh
#!/bin/bash
cp -r /etc/letsencrypt/ /backup/letsencrypt-$(date +%Y%m%d)/
# 查看续期日志
cat /var/log/letsencrypt/letsencrypt.logacme.sh 自动续期
# acme.sh 默认每天检查并自动续期
# 查看定时任务
crontab -l | grep acme
# 修改续期后的重载命令
acme.sh --install-cert -d example.com \
--key-file /etc/ssl/private/example.com.key \
--fullchain-file /etc/ssl/certs/example.com.crt \
--reloadcmd "systemctl reload nginx"
# 设置续期通知
acme.sh --set-notify \
--notify-hook mail \
--notify-to admin@example.com \
--notify-level 2
# 升级 acme.sh
acme.sh --upgradeDocker 环境下的证书续期
# docker-compose.yml — 自动续期方案
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- cert-data:/etc/letsencrypt
- web-root:/var/www/html
certbot:
image: certbot/certbot
volumes:
- cert-data:/etc/letsencrypt
- web-root:/var/www/html
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
volumes:
cert-data:
web-root:SSL 安全检测与验证工具
# 使用 SSL Labs 在线检测(推荐)
# 访问 https://www.ssllabs.com/ssltest/
# 输入域名即可获得 A-F 评级
# 检测项:协议版本、密码套件、证书链、HSTS、OCSP 等
# 使用 openssl 命令行检测
# 1. 检查服务器支持的 TLS 版本
for version in tls1 tls1_1 tls1_2 tls1_3; do
echo -n "$version: "
openssl s_client -connect example.com:443 -$version </dev/null 2>&1 | grep -q "Protocol" && echo "支持" || echo "不支持"
done
# 2. 检查证书链完整性
openssl s_client -connect example.com:443 -showcerts </dev/null | \
awk '/BEGIN CERT/{print "证书: " (++n)} /subject=/'
# 3. 检查证书的 SAN(Subject Alternative Names)
openssl s_client -connect example.com:443 </dev/null 2>/dev/null | \
openssl x509 -noout -ext subjectAltName
# 4. 测试特定密码套件
openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384' </dev/null
# 5. 检查 OCSP Stapling 是否启用
openssl s_client -connect example.com:443 -status </dev/null 2>&1 | \
grep -A 2 "OCSP response"
# 6. 使用 testssl.sh 全面检测(推荐命令行工具)
# 安装
git clone https://github.com/drwetter/testssl.sh.git
cd testssl.sh
# 全面检测
./testssl.sh example.com
# 只检测协议
./testssl.sh -P example.com
# 只检测漏洞
./testssl.sh -U example.com
# 输出 JSON 报告
./testssl.sh --json example.com常见 SSL 错误排查
# 错误 1:证书链不完整
# 现象:浏览器显示正常,但 curl 报错
curl: (60) SSL certificate problem: unable to get local issuer certificate
# 排查:检查证书链
openssl s_client -connect example.com:443 -showcerts </dev/null
# 解决:合并证书链
cat server.crt intermediate.crt > fullchain.crt
# 错误 2:证书域名不匹配
# 现象:证书是给 www.example.com 签发的,但用 example.com 访问
curl: (51) SSL: no alternative certificate subject name matches target host name
# 解决:申请包含 SAN 的证书
# Let's Encrypt 默认支持 SAN
certbot certonly -d example.com -d www.example.com
# 错误 3:证书过期
# 现象:浏览器显示"您的连接不是私密连接"
# 排查
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# 解决:检查自动续期是否正常
certbot renew --dry-run
systemctl status certbot.timer
# 错误 4:混合内容(Mixed Content)
# 现象:HTTPS 页面加载 HTTP 资源,浏览器地址栏显示"不安全"
# 解决:将所有资源链接改为 HTTPS 或使用相对协议
# 错误 5:SNI(Server Name Indication)配置缺失
# 现象:一台服务器托管多个 HTTPS 站点,访问错误的证书
# 排查
openssl s_client -connect example.com:443 -servername example.com </dev/null
# 解决:确保 Nginx 配置了正确的 server_name
# server {
# server_name example.com;
# ssl_certificate /path/to/example.com.crt;
# }#!/bin/bash
# check-cert-expiry.sh — 检查证书到期时间
DOMAINS="example.com api.example.com app.example.com"
ALERT_DAYS=30
for domain in $DOMAINS; do
expiry=$(echo | openssl s_client -servername "$domain" -connect "$domain":443 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
if [ -n "$expiry" ]; then
expiry_epoch=$(date -d "$expiry" +%s)
now_epoch=$(date +%s)
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
echo "$domain: 剩余 ${days_left} 天"
if [ "$days_left" -le "$ALERT_DAYS" ]; then
echo "WARNING: $domain 证书将在 ${days_left} 天后过期!"
# 发送告警邮件
# echo "$domain 证书将在 ${days_left} 天后过期" | mail -s "SSL证书即将过期" admin@example.com
fi
else
echo "ERROR: 无法获取 $domain 的证书信息"
fi
done优点
缺点
总结
SSL/TLS 与 HTTPS 配置是 Web 安全的基石。通过 Let's Encrypt 获取免费证书,使用 Certbot 或 acme.sh 实现自动化申请与续期,配合 Nginx 的 Intermediate 级别 SSL 配置和 OCSP Stapling 优化,能够在保障安全的同时兼顾性能。建议定期检查证书到期时间,配置自动续期并设置失败告警,使用 SSL Labs 等工具验证配置安全性,确保网站始终处于安全可靠的 HTTPS 保护之下。
关键知识点
- DevOps 主题的核心是让交付更快、更稳、更可审计。
- 自动化不是把命令脚本化,而是把失败、回滚、权限和观测一起设计进去。
- 生产链路必须明确制品、环境、凭据、配置和责任边界。
- 安全类主题的关键不只在认证成功,而在于权限边界、证书信任链和审计链路是否完整。
项目落地视角
- 把流水线拆成构建、测试、制品、部署、验证和回滚几个阶段。
- 为关键步骤补齐日志、指标、通知和人工兜底点。
- 定期演练扩容、回滚、故障注入和灾备切换。
- 明确令牌生命周期、刷新策略、作用域、Claims 和失败返回模型。
常见误区
- 只关注部署成功,不关注失败恢复和审计追踪。
- 把环境差异藏在临时脚本或人工操作里。
- 上线频率高了以后,没有标准化制品和配置管理。
- 只验证登录成功,不验证权限收敛和令牌失效场景。
进阶路线
- 继续补齐 GitOps、可观测性、平台工程和成本治理。
- 把主题和应用架构、安全、权限、备份恢复联动起来理解。
- 形成团队级平台能力,而不是每个项目重复造轮子。
- 继续深入零信任、细粒度授权、证书自动化和密钥轮换。
适用场景
- 当你准备把《SSL/TLS 与 HTTPS 配置》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合构建自动化交付、基础设施治理、监控告警和生产发布体系。
- 当团队规模扩大、发布频率提升或环境变多时,这类主题会显著影响交付效率。
落地建议
- 所有自动化流程尽量做到幂等、可审计、可回滚。
- 把制品、变量、凭据和执行权限分层管理。
- 定期演练扩容、回滚、密钥轮换和灾备恢复。
排错清单
- 先定位失败发生在代码、构建、制品、环境还是权限层。
- 检查流水线变量、凭据、镜像标签和目标环境配置是否一致。
- 如果问题偶发,重点看并发发布、资源争抢和外部依赖抖动。
复盘问题
- 如果把《SSL/TLS 与 HTTPS 配置》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《SSL/TLS 与 HTTPS 配置》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《SSL/TLS 与 HTTPS 配置》最大的收益和代价分别是什么?
