十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

HTTPS证书告警排查指南:从证书链到TLS握手与监控

HTTPS证书告警排查指南:从证书链到TLS握手与监控 我做了近十年的站点运维但第一次被浏览器那个刺眼的红色警告吓得后背发凉是在一次大促前夕。用户反馈一条接一条全是网址栏写着不安全的截图。整个团队盯着监控面板却没人说得清证书到底哪一步出了问题。那次之后我养成了一个习惯凡是域名上挂了HTTPS必须先搞清楚浏览器那个红色警告背后的机制再谈配置和优化。这篇文章就是基于这些年处理过的SSL踩坑案例整理的。覆盖证书过期、证书链不完整、协议版本不匹配、加密套件漏洞这类高频问题也会给出我在真实环境里验证过的排查命令和自动化监控方案。无论你是刚把站点迁到HTTPS的新手还是被线上告警折磨过几次的运维老手应该都能从这里找到直接能用的应对思路。1. 浏览器那个红色警告到底在警告什么在动手改配置之前先得把浏览器验证书的机制捋清楚。很多人一看到您的连接不是私密连接就慌了其实浏览器只做了三件事检查证书是否可信、域名是否匹配、证书是否在有效期内。三步里任何一步不通过就会触发红色警告。1.1 证书信任链你的站点证书不是自证清白HTTPS的核心是SSL/TLS证书。证书的作用不是加密本身而是为加密所用的公钥做担保。打个比方你和用户之间要用一把锁通信证书就是这把锁上的生产厂家标签浏览器需要确认这个标签确实是厂家贴的而不是别人乱贴的。这个确认过程就是证书链校验。浏览器内置了一批根证书颁发机构CA的信任锚点。你的站点证书叶子证书通常不是根CA直接签发的而是由一个中间CA签发中间CA再由根CA签发。浏览器拿到站点证书后会尝试构建一条从叶子证书到根证书的信任链。链路中任何一环缺失、过期、被吊销都会导致校验失败。我在实际排障中发现证书链不完整是最容易被忽视的问题。很多管理员只把站点证书通常是cert.pem配了上去忽略了中间证书chain.pem。用户的浏览器倒是很诚实——它在本地找不到中间证书又不敢擅自信任你的站点证书于是直接亮红牌。1.2 域名匹配浏览器不是只看长得像不像第二个常见问题是域名不匹配。浏览器不只是看看证书上印的域名和你访问的域名“是不是同一个”而是有严格的匹配规则。证书上的域名写在subjectAltNameSAN字段里。比如你申请了一张证书上面写了example.com和www.example.com那么用户通过这两个域名访问都没有问题。但如果你用IP地址访问或者用了blog.example.com而证书里没有这个域名浏览器一样报错。这里有个技巧*.example.com这种泛域名证书可以覆盖blog.example.com、shop.example.com等等但它不能覆盖example.com本身。所以选泛域名证书的时候一定要确认证书是否同时包含了裸域名。我见过不止一个团队买了泛域名证书结果主站挂了example.com照样飘红。1.3 有效期检查一天也不能差证书有效期是第三个检查项。浏览器会对比本地时间和证书的notBefore、notAfter字段。这里要注意浏览器用的是访问者的本地时间。如果某个用户的电脑时间错了、时区没设对也会导致合法证书被判定为过期。这种情况虽然不常见但确实会出现。真正的坑在于很多过期告警并不是管理员不知道证书会过期而是没建有效的预警机制。免费证书的有效期通常是90天或一年一忙起来就忘了续期。我在后面会专门讲怎么把续期这件事做成半自动甚至全自动。2. 证书过期最常见的“红屏”触发器与续期自动化证书过期是生产环境里出现红色警告的最大单一原因。而且它很有意思——很多技术团队能在几分钟内定位到问题但就是没拦住它发生。核心原因通常是两个证书数量多、分散部署全靠人工记录时间或者续期流程过长中间环节被卡住了。2.1 你的证书到底哪天到期三分钟摸清家底排查证书过期的第一步是搞清楚手里到底有多少张证书、各自什么时候到期。我常用下面这组命令快速摸底# 查看本地证书文件的有效期 openssl x509 -in yourdomain.pem -noout -dates # 直接查看线上站点的证书有效期不下载文件 echo | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2/dev/null | openssl x509 -noout -dates # 批量扫描多个域名 for domain in a.com b.com c.com; do echo $domain: echo | openssl s_client -servername $domain -connect $domain:443 2/dev/null | openssl x509 -noout -enddate done不要只在问题发生时才跑。建议把这组命令写成一个脚本配到定时任务里每周跑一次输出结果发到团队的告警群里。对于中小站点这就足够提前发现问题了。2.2 阿里云免费证书的续期节奏与常见遗漏目前国内用得最多的免费证书渠道应该就是阿里云的数字证书管理服务。免费证书有效期是一年这已经比之前90天的周期宽裕很多了。但它的问题在于续期是全手工的操作系统不会自动帮你换。我踩过的坑是这样的有一年阿里云推短信提醒我以为续期后会像以前一样自动同步到服务器。结果因操作时只下载了新的证书文件并在控制台做了部署但负载均衡SLB上的旧证书还是没被替换前后端的证书不一致导致部分接口调用直接握手失败。这里提醒一下阿里云控制台上“部署”到某个产品比如SLB、CDN只代表把这个证书推送到了云产品的证书库不一定会自动替换正在使用的监听器配置。你需要在负载均衡管理控制台的“证书管理”里确认监听器使用的是不是最新上传的那张证书。这个步骤非常容易被漏掉。2.3 全自动续期的实践让服务器自己换证如果你使用的是Lets Encrypt这类有ACME协议的证书签发服务续期可以做到全自动。核心思路就是装一个ACME客户端让它定期检查证书余量低于30天就自动续期并重载服务。最常见的工具是certbot我现在的服务器基本都用它# 安装 certbot以 Nginx 为例 apt install certbot python3-certbot-nginx # 签发或续期证书certbot 会自动改写 Nginx 配置 certbot --nginx -d yourdomain.com -d www.yourdomain.com # 测试自动续期任务是否正常 certbot renew --dry-runcertbot renew通常会被安装在系统的定时任务里每天跑两次。它只会在证书剩余有效期不足30天时才会真正续签其他时间都直接跳过非常省心。如果你的环境用了OpenResty、Caddy或者自研网关续期后的重载方式可能不同。记得在续期钩子里加上对应的重载命令比如certbot renew --deploy-hook nginx -s reload这个deploy-hook是只在新证书部署成功后才会触发的钩子比把重载命令写在每个任务里要安全得多。3. 握手失败排查协议版本、加密套件与常见兼容性陷阱证书本身没问题但用户访问依然报错这种情况大多和TLS握手有关。握手是HTTP和HTTPS最大的区别所在也是配置最容易出错的地方。3.1 一次完整的TLS握手从ClientHello到加密通道建立先把握手流程简化为四个步骤客户端发起ClientHello告诉服务器自己支持的TLS版本和加密套件列表。服务器回复ServerHello选定一个双方都支持的版本和套件同时下发自己的证书链。客户端验证证书链有效性然后生成一个临时密钥用服务器的公钥加密后发送过去。双方用这个临时密钥协商出会话密钥之后的数据都用会话密钥加密。绝大部分握手失败都发生在第1步和第2步。尤其是版本协商这一步服务器如果只支持TLS 1.2而客户端只发送了TLS 1.3双方就会直接断开。你会在服务器日志里看到类似ssl handshake failed、no shared cipher这样的错误。3.2 新版浏览器与老服务器的“代沟”这几年浏览器厂商在不断收紧TLS版本要求。主流的Chrome、Firefox、Edge都已经明确要放弃TLS 1.0和TLS 1.1的支持。如果你的服务器还在用这两个老版本用户的浏览器会因为无法完成握手而直接拒绝连接。有个很典型的场景公司内网有一套旧系统跑在Windows Server 2008 老版本OpenSSL上只支持TLS 1.0。平时用IE时代的老浏览器都能正常访问某天用户换了台新电脑、装了新版Chrome打开系统直接白屏网络抓包一看ClientHello里最高只支持TLS 1.3服务器却只能回应TLS 1.0两边鸡同鸭讲。解决思路有两个短期在前端加一层Nginx或HAProxy做TLS终结由Nginx对外提供TLS 1.2/1.3再通过内网HTTP转发给老系统。这样不动老应用也能保证外部访问安全。长期推动业务系统升级运行库否则这一层中间代理会一直成为新的故障点。3.3 加密套件不匹配为什么“安全性最高”反而连不上握手失败的另一个常见原因是加密套件不匹配。加密套件决定了密钥交换、身份验证、对称加密和消息认证这几个环节各用什么算法。服务器和客户端必须选出一套双方都认识的组合否则也是握手失败。有些安全团队出于“更安全”的考虑把服务器端的加密套件列表收得很窄只留了几个高强度套件。结果就是老客户端的兼容性急剧下降。比如有些客户用的是老款Android系统它们只支持ECDHE_RSA_WITH_AES_128_GCM_SHA256而不支持你只开放的TLS 1.3套件于是直接被拒绝。配置加密套件的原则是保留一个适合绝大多数现代客户端的核心集合而不是把列表砍到最精简。Nginx里可以用下面这个级别的配置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; ssl_prefer_server_ciphers on;这里特意没有关闭TLS 1.2就是为了兼容那些还没支持TLS 1.3的客户端。安全团队如果要做合规扫描只要把TLS 1.0和1.1关掉问题通常都不大。3.4 识别SSLHandshakeException到底卡在哪一步Java应用里常见的报错是javax.net.ssl.SSLHandshakeException但具体卡在哪一步光看异常信息很难判断。我提供一个排查思路先用命令行工具模拟一次握手输出完整细节openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -tls1_2把-tls1_2换成-tls1_3再跑一遍。如果其中一个版本成功、另一个失败说明是版本协商问题。如果两种都失败再在命令后面加-showcerts看看证书链是否返回完整。如果提示verify error就要回到第1章说过的证书链和域名匹配去检查。还有一类比较隐蔽的情况应用服务器本身配的密钥库Java通常用JKS或PKCS12有问题。比如证书导入密钥库时密码不对或者密钥库中同时存在多个过期证书也会产生让人摸不着头脑的握手异常。遇到这种情况可以用keytool查看keytool -list -v -keystore yourkeystore.jks -storepass changeit重点看Valid from和Valid until字段确认应用实际使用的证书没有过期。4. 证书链与格式转换部署时最容易被忽略的环节证书链问题我已经提过几次了这里展开聊透顺便把部署中涉及的格式转换讲清楚。很多部署事故其实发生在证书下载之后、配置生效之前这个中间阶段。4.1 一张证书的“全家福”根证书、中间证书与叶子证书拿到一张证书文件先别急着传到服务器。证书文件内容是人眼可读的我建议打开看一眼。通常Java和Tomcat的部署包里会有.jks或.p12文件Nginx和Apache多是.pem、.crt或.cer文件。一份完整的证书链文件fullchain应该包含-----BEGIN CERTIFICATE----- 你的域名证书叶子证书 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- 中间CA证书 -----END CERTIFICATE-----如果只有叶子证书没有中间证书很多客户端也能通过“证书链发现”机制去拉取中间证书但有些网络环境下这种行为会被阻断反过来又造成校验失败。所以我的建议是不管客户端能不能自动找到中间证书服务器端都把完整链配好做到自给自足。4.2 Nginx和Tomcat的证书配置差异Nginx配证书是一行一证书server { listen 443 ssl; server_name yourdomain.com; ssl_certificate /etc/nginx/certs/fullchain.pem; ssl_certificate_key /etc/nginx/certs/yourdomain.key; }ssl_certificate就是full chain文件ssl_certificate_key是私钥。Tomcat则通常需要把证书和私钥打包成PKCS12格式.p12或.pfx然后在server.xml里配置Connector port8443 protocolorg.apache.coyote.http11.Http11NioProtocol maxThreads150 SSLEnabledtrue SSLHostConfig Certificate certificateKeystoreFileconf/yourdomain.p12 certificateKeystorePasswordyourpassword typeRSA / /SSLHostConfig /Connector很多人在这步直接把.pem文件指到Tomcat的keystoreFile里然后报错。因为Tomcat默认要求密钥库格式而不是裸证书格式。下文讲到的.cer转.pfx就是专门针对这类场景。4.3.cer和.pfx互转一条命令解决格式烦恼有段时间群里经常有人问“拿到了一张.cer证书怎么装到Tomcat”“cer怎么转成pfx”。这里把通用转换命令列一下。前提是你手上有一张证书文件.cer或.crt和对应的私钥文件.key。# 将 .cer .key 转换为 PKCS12.pfx此命令同时适用于 Tomcat 和 Windows IIS openssl pkcs12 -export -in yourdomain.cer -inkey yourdomain.key -out yourdomain.pfx -name yourdomain -passout pass:YourPassword转换完成后可以用keytool导入到JKSkeytool -importkeystore -srckeystore yourdomain.pfx -srcstoretype PKCS12 -destkeystore yourdomain.jks -deststoretype JKS整个过程不难但要注意两个细节私钥和证书必须匹配。校验方法很简单用openssl x509 -noout -modulus -in yourdomain.cer和openssl rsa -noout -modulus -in yourdomain.key对比输出的MODULUS完全一致才说明匹配。转换时的密码不要用太简单的否则密钥库的安全性会拖累整体安全评分。4.4 部署完成后的自检我每次都会跑的三条命令配置改完、服务重载之后不要急着收工。我会依次跑三条命令做自检第一条检查证书链是否完整openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -showcerts /dev/null看输出里是否包含s:CN yourdomain.com和i:CN ...的CA签发者并且确认证书链里有多层证书。第二条确认协议版本nmap --script ssl-enum-ciphers -p 443 yourdomain.com如果没有nmap可以用openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -tls1_3 /dev/null第三条确认域名匹配。这一步用浏览器的“开发者工具”最直观——打开Network面板刷新页面看请求的“Security”标签里有没有显示“Certificate is valid”相关的信息。5. 用工具把隐患提前挖出来从原理扫描到监控告警等到用户来投诉再排查已经晚了。做这行时间越长我越明白“预防”两个字的分量。这里推荐一套组合拳可以让证书问题在影响用户之前就被处理掉。5.1 漏洞扫描中常见的TLS问题比如CVE-2016-2183安全扫描报告里经常出现一个看着很吓人的条目SSL/TLS协议信息泄露漏洞(CVE-2016-2183)。这个漏洞本质上和3DES加密套件有关。3DES算法的密钥强度在现代计算能力下已经不够安全扫描器发现服务器支持3DES套件就会报这个漏洞。修复方式很简单就是把3DES系列套件从配置里禁用。以Nginx为例ssl_ciphers HIGH:!aNULL:!MD5:!3DES;注意配置文件里不要同时用多个ssl_ciphers指令后者会覆盖前者。修改完后要重新加载Nginxnginx -t nginx -s reload改完后重新扫描这一项就消失了。5.2 市面上几个实用的SSL在线检测与本地检测工具除了自写脚本还有一些工具可以帮你做更全面的体检。在线类的SSL Labs输入域名它会从多个角度打分证书链、协议版本、加密套件、已知漏洞。每次检测完会给出A、A-、B等评级。这个评级在跟安全团队解释“为什么我的站点安全”时很有说服力。本地Classsslscan和testssl.sh是我用得最多的。testssl.sh是绿色单文件工具支持详细到每一类握手包的检查甚至能测出HEARTBLEED这类漏洞。适合在无法把域名暴露到公网的内网环境里使用。# 下载并运行 testssl.sh git clone https://github.com/drwetter/testssl.sh.git cd testssl.sh ./testssl.sh yourdomain.com:4435.3 自建证书监控从脚本到告警的完整链路如果是多个域名、多张证书手工去每个平台查看有效期太费劲了。我会建议自己搭一个轻量级的监控脚本。整个流程很简单定时任务跑脚本读取证书到期时间计算出剩余天数低于阈值就发送告警消息企业微信/钉钉/Slack都行。下面这个脚本是我在一台跳板机上跑的#!/bin/bash domains(a.com b.com c.com) threshold_days30 for domain in ${domains[]}; do expiry_date$(echo | openssl s_client -servername $domain -connect $domain:443 2/dev/null | openssl x509 -noout -enddate | cut -d -f2) expiry_epoch$(date -d $expiry_date %s) now_epoch$(date %s) remain_days$(( (expiry_epoch - now_epoch) / 86400 )) echo $domain 剩余 ${remain_days} 天 if [ $remain_days -lt $threshold_days ]; then curl -s -X POST \ https://your-webhook-url \ -H Content-Type: application/json \ -d {\msgtype\:\text\,\text\:{\content\:\证书到期提醒: $domain 剩余 ${remain_days} 天\}} fi done脚本很粗糙但稳定跑了两年。如果你需要更丰富的功能比如把到期时间写进日志文件、支持多证书链告警可以在此基础上扩展。5.4 常见告警类型清单与应对策略把平时积累的告警类型和应对策略整理成了一个表格方便快速对照告警/报错信息可能原因优先排查方向证书过期NET::ERR_CERT_DATE_INVALID未续期或客户端时间错误用openssl看证书记载的到期时间证书不受信任NET::ERR_CERT_AUTHORITY_INVALID证书链缺失或自签名证书检查fullchain是否包含中间证书域名不匹配NET::ERR_CERT_COMMON_NAME_INVALIDSAN字段缺少访问域名检查证书覆盖的域名列表握手失败SSL handshake failedTLS版本不匹配或加密套件不一致用openssl分别测TLS 1.2/1.3扫描报CVE-2016-2183支持3DES套件在ssl_ciphers里禁用3DESJava SSLHandshakeException密钥库证书过期/密钥库密码错误用keytool -list -v检查密钥库内容部分接口报错IOS/Android客户端兼容性差异检查加密套件是否过窄6. 实战复盘一次完整排查一个真实站点的HTTPS飘红理论知识讲了不少最后挑一个我处理过的真实案例做全流程复盘。这个案例不算复杂但几乎串联了前面提到的所有关键知识点。6.1 现象所有浏览器统一报“不安全”和“证书无效”那天上午社区运营反馈说网站打不开了用户截图上显示的是NET::ERR_CERT_AUTHORITY_INVALID。我先确认了问题的影响面PC端Chrome、Edge、手机端微信内嵌浏览器全部报错说明不是单一客户端的问题而是服务器端证书配置出了状况。6.2 排查过程先看证书链再查服务日志第一步我直接连接服务器443端口尝试获取证书链openssl s_client -connect ops.example.com:443 -servername ops.example.com -showcerts /dev/null输出里明显少了中间证书。服务器返回的链只有一层叶子证书没有CA的中间证书。第二步我登录Nginx服务器找到虚拟主机配置文件发现ssl_certificate指向的文件只包含了站点证书内容没有拼接中间证书。第三步我找到证书提供商在邮件里发的“Nginx版本”下载包里面其实给了三个文件_public.crt、_chain.crt和_key.key。当时的错误很明显配置时少了_chain.crt。6.3 修复与验证一分钟改完半个钟观察修复方式是把两个证书文件拼成一个cat yourdomain_public.crt yourdomain_chain.crt fullchain.pem然后把Nginx配置改成ssl_certificate /etc/nginx/certs/fullchain.pem; ssl_certificate_key /etc/nginx/certs/yourdomain.key;重载nginx -t nginx -s reload再用openssl s_client重新验证输出里出现了两段BEGIN CERTIFICATE浏览器恢复正常访问。当天为了确保没有其他潜在问题我继续观察了半个钟确认无新增告警才算收工。6.4 复盘提炼三个能够提前拦住这场事故的动作事后总结这场故障完全可以在30天前就避免。三个动作分别是第一次配置完证书后用openssl s_client -showcerts做一次完整自检。建立周期性的证书到期和链完整性巡检而不是只盯到期时间。在团队内部把“证书更新必须同时更新所有CDN、SLB、网关节点”写进流程清单。如果你现在正准备部署一张新证书建议把这几条当成默认动作而不是“以后有空再做”。HTTPS这件事本质上不是配一次就一劳永逸的。证书会到期、浏览器标准在升级、安全扫描工具越来越敏锐每一项变化都可能让原本稳定的站点突然飘红。每次看到那个红色警告我的第一反应已经不是慌张而是按流程打开终端、跑一遍检查命令快速锁定问题出在哪一环。希望这篇内容能帮你少走一些我走过的弯路至少在遇到红色警告时心里有个清晰的排查地图。
返回列表