SSL 证书排查清单
主机名匹配、有效期、证书链、TLS 版本、跳转和混合内容——浏览器告警出现时按这个顺序排查。
从浏览器报错开始,不要相信本地信任库
本机浏览器和 `openssl` 经年累月装了一堆信任锚、中间证书缓存、系统证书包。本机能打开的网站,别人不一定能打开。
验证时要用不共享本机信任库的环境:另一台机器、另一个系统、在线检查工具、CI runner 都行。浏览器告警才是真相,本机终端不是。
确认主机名被覆盖
证书必须列出用户实际访问的主机名——要么在 CN(已废弃但仍常见),要么在 Subject Alternative Name (SAN) 扩展中。现代浏览器已完全忽略 CN,只看 SAN。
通配符 `*.example.com` 只匹配一级。它匹配 `www.example.com` 和 `api.example.com`,但既不匹配根 `example.com`,也不匹配 `staging.api.example.com`。多级通配符是不允许的。
# 列出证书覆盖的所有主机名:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -ext subjectAltName 检查有效期和续期自动化
过期证书是最显眼的故障。Let's Encrypt 90 天,商业 CA 一般 397 天,行业趋势在缩短——续期必须自动化。
不仅看过期时间,还要确认续期任务真的在跑。一张剩 30 天但从没续过期的证书就是定时炸弹。监控的是续期日志,不是过期日期。
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -dates
# notBefore=Jan 1 ...
# notAfter=Apr 1 ... 验证证书链完整
证书有效需要客户端能构建出一条到信任根的链。服务器必须发叶子证书 + 所有中间证书;根证书在客户端信任库里。忘配中间证书是"Chrome 能开但 iOS 打不开"最常见的原因。
从本机信任库之外测。`ssllabs.com/ssltest/` 或 `whatsmychaincert.com` 这种工具能精确报告服务器实际发了什么、缺了什么。
# 显示服务器发的完整链:
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
# 验证链能到信任根:
curl -v https://example.com 2>&1 | grep -i "ssl\|tls\|verify" TLS 版本与加密套件
TLS 1.0 和 1.1 已废弃,主流浏览器全部移除。TLS 1.2 是今天的下限,TLS 1.3 是默认。还允许 1.0/1.1 的话,安全扫描器和合规框架都会报红。
弱加密套件(RC4、3DES、出口级)早该清理。现在服务器默认配置基本合理,但参考老教程改的配置偶尔会复活这些。
# 探测服务器支持的协议与套件:
nmap --script ssl-enum-ciphers -p 443 example.com
# 或用 openssl:
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3 跳转与 HSTS
纯 HTTP 必须跳转到 HTTPS。验证跳转链——有时会多一跳 www 去除或末尾斜杠规范化,边缘场景会断。
HTTPS 稳定后再加 HSTS,让浏览器拒绝降级到 HTTP。注意 `preload` 指令基本是永久的,先不带 preload 跑几周再考虑加。
# 跟踪跳转链:
curl -sIL http://example.com | grep -i "location\|HTTP/"
# 检查 HSTS 头:
curl -sI https://example.com | grep -i "strict-transport-security" 混合内容
证书有效不代表浏览器会显示"完全安全"。HTML 里只要有一个 `http://` 的图片、脚本、样式表或 iframe,浏览器就会降级锁标。
搜代码里写死的 `http://` URL,换成协议相对(`//example.com/path`)或绝对 HTTPS。浏览器 DevTools → Console 会在页面加载时列出所有混合内容警告。