用 testssl.sh 给线上站点做 TLS 体检:它查什么、怎么跑、结果怎么看
浏览器的小绿锁只能说明证书没过期,协议版本、弱密码套件、证书链问题都藏在底下。本文讲清 testssl.sh 的检查范围、常用跑法和输出中哪些结果该紧张、哪些可以先放放。
本文目录

线上告警最常见的一种是:"证书还有 20 天过期",换了证书、重载 nginx,告警消了,大家散场。但两周后某个旧安卓客户端连不上你的 API,或者安全扫描报告甩过来一句"检测到弱密码套件"。证书没问题 ≠ TLS 配置没问题。这时候该跑一次 testssl.sh,而不是再续一次证书。
它能查什么
testssl.sh 是一个纯 bash 脚本,不依赖浏览器,直接对目标做握手和探针。它的检查范围大致分四类:
- 证书:有效期、签发链是否完整、签名算法(还在用 SHA-1 就该换了)、域名匹配。
- 协议版本:从 SSLv2 一路试到 TLS 1.3,列出服务端实际支持哪些。看到 SSLv3 或 TLS 1.0 可用,基本就是整改项。
- 密码套件:按协议逐个握手枚举,标出 NULL、EXPORT、RC4、3DES、CBC 这类弱或已弃用的套件。
- 已知漏洞:Heartbleed、POODLE、BEAST、ROBOT、Logjam 等,逐项给出 vulnerable / not vulnerable。
它不查的东西也要心里有数:它不看你的私钥权限、不评价 HSTS 之外的 HTTP 安全头配置是否"合理",更不替你决定业务上能不能砍掉 TLS 1.0——那是兼容性决策,不是扫描结果。
怎么跑
装法很随意,clone 仓库或者发行版包都行。最常用的一条命令:
./testssl.sh --fast example.com:443--fast 跳过部分耗时检查,适合日常巡检。要完整报告就裸跑:
./testssl.sh example.com
几个运维场景里实用的开关:-U 只查漏洞项,适合变更后的快速回归;--jsonfile out.json 输出结构化结果,方便接进巡检脚本做对比;测非 HTTPS 端口(比如 25、993)时加 --starttls smtp。对内网自签证书的服务,用 --insecure 跳过证书信任校验,但别拿这个结果对外说"证书没问题"。
结果怎么读
输出是彩色分级的,原则很简单:红色必须处理,黄色看业务,绿色和蓝色是信息。
几个常见的误读:
- "TLS 1.0 offered" 标红,但如果你的站点还必须服务某个老客户端,这不是 bug,是记录下来的取舍。改之前先查访问日志里还有多少旧协议握手。
- 密码套件列表很长不代表不安全,看它标红的行。如果只有一两个弱套件但排在优先级末尾、实际几乎不会被协商到,风险有限——不过既然留着没用,删掉更省心。
- 漏洞项里 "not vulnerable (timed out)" 这类结果意味着探测本身没完成,不要当成"安全"。换个网络或去掉限速再跑。
- 证书链不完整(chain issues)在浏览器里常常被系统缓存的中间证书掩盖,testssl.sh 会如实报出来。这是它最有价值的输出之一,很多移动端和 API 客户端的"偶发握手失败"根因就在这。
在 CertbotX 中实践
扫描发现证书快过期或链有问题时,痛点通常不在"知道",而在"改完所有节点"。CertbotX 覆盖的是这一段:通过 DNS-01 的 CNAME 委托完成域名验证(适合没有权限改主 DNS 或需要泛域名的场景),由 ACME 自动签发并在到期前重新签发,然后把新证书自动部署到 Agent、EdgeOne 或 CDN,每次部署留有记录可审计。也就是说,testssl.sh 告诉你"证书 15 天后过期、链缺中间证书",CertbotX 负责让下一个周期不再靠人盯着日历换证书——但协议和密码套件的整改,仍然是 nginx 配置里的活。
收尾的一句
把 testssl.sh 加进季度巡检,跑一次全量、留一份 JSON 基线,下次对比差异,比每次出事再临时跑要省事得多。TLS 配置是"一次改对、长期受益"的类型,它不像证书那样天天催你续费——所以它的问题也更容易被遗忘。