2.4 HTTPS 与证书:全站加密

场景:证书过期之夜

如果说网关建设里哪件事最不该出但偏偏出了,那就是证书过期。某个周六晚上十一点,群里有客户问接口是不是挂了,值班同事一看监控全是 TLS 握手失败——网关的域名证书当天到期。我们买的是一年期证书,没人设提醒,到期即停服。全员折腾到凌晨三点才换好新证书。第二天复盘会上立下军令状:所有证书集中托管加自动告警,缺一天都不行。

这件事之外,我们还多做了一步:全站强制 HTTPS,任何 HTTP 请求一律 301 跳 HTTPS,杜绝明文凭据在公网裸奔。前面的签名校验防的是篡改与重放,HTTPS 防的是窃听,两者缺一不可,少了任何一层,另一层的防线都会被绕过。

方案:证书托管与加密套件

证书方案我们对比了三条路。第一是手动申请免费证书,一年期,需要人工记得换,出局——那次事故就是这么来的。第二是付费买多年期证书,贵,但依然没有告警兜底。第三是腾讯云 SSL 证书服务加 CDN/负载均衡侧托管,证书由平台统一管理与提醒,续期在控制台点一下,监控里还能看到到期天数。我们选了第三条:

方案 成本 到期告警 最终选择
手动免费证书 零 全靠人记 出局
多年期付费证书 高 无 备用
云 SSL 托管 低 平台自动告警 采用

加密套件方面,Nginx 只保留 TLS 1.2 与 1.3,1.0 与 1.1 直接关掉。虽然牺牲了极少数古董客户端,但安全收益大于一切。HTTP/2 顺手开启,多路复用对高并发小响应特别友好。

从成本角度也值得一提。开启 HTTP/2 与长连接复用之后,TLS 握手次数大幅下降,CLB 与网关的 CPU 占用降了约三成,相当于省了一台机器的钱。加密不是纯支出,配置得当的加密反而能摊薄整体成本,这条经验后来写进了公司的技术规范第一条。

配置:Nginx 全站 HTTPS

配置部分每行都来自实战。HSTS 头是一次性把后续请求全锁定 HTTPS 的关键,加上之后客户端会记住一年;OCSP 装订减少握手延迟;加密套件只留高强度组合:

server {
    listen 80;
    server_name api.example.com;
    return 301 https://$host$request_uri;   # 强制跳转
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_certificate     /etc/tls/fullchain.pem;
    ssl_certificate_key /etc/tls/privkey.pem;
    ssl_stapling on;
    ssl_stapling_verify on;

    add_header Strict-Transport-Security
        "max-age=31536000" always;

    location / { proxy_pass http://127.0.0.1:9000; }
}

证书巡检清单

那次事故之后我们建立了每周巡检,五个检查项固定锚定在每周三上午十点,跑脚本出一页报告,异常直接发群。清单看起来朴素,但证书这种东西就是靠纪律而非智商守住:所有异常项都直接推送到值班群,并在工单系统生成跟踪单,直到评级恢复 A 级才允许关单:

频次 工具 输出
每周 巡检脚本 一页报告
每月 人工抽查 复核签字
每季 演练 模拟证书紧急替换

季度演练是最有价值的环节:从接到告警到 CLB 换好备用证书,我们目前的耗时是十一分钟。没有演练的话,第一次实战绝不会是这个速度。

检查项 红线
到期天数 不足 30 天
覆盖域名 与路由表完全一致
绑定资源 CLB 与 CDN 全部生效
评级 SSL 检测低于 A
跳转链路 HTTP 一律 301 到 HTTPS

踩坑:HSTS 的双刃剑

HSTS 是双刃剑,开的第一天就有客户在公司内网代理后调接口报错(代理做了中间人解密,被 HSTS 直接拒绝)。折中方案是域名层面分开:对外的 api.example.com 开 HSTS,内网测试域名不开。第二个坑是证书链不全,只配了证书没配中间证书,导致部分手机端浏览器报证书不可信。规范做法是 fullchain 一次配全,并用公开的 SSL 检测工具跑一遍评级,目标拿到 A 及以上。

第三个坑是文档站忘了配 HTTPS。接口都加密了,文档站还是 HTTP,客户在文档页贴的密钥截图照样能被嗅探。后来文档站一起上了 HTTPS 并挂 CDN(CDN 文档入口 cloud.tencent.com/document/product/228),CDN 侧自动托管证书加速两不误,海外客户打开速度还快了三倍。

第四个坑是 TLS 握手耗时。PHP 网关到后端的调用我们也走了 HTTPS,刚上线没注意连接复用,每个请求都重新握手,高峰期 CPU 飙高。开启 guzzle 的连接池与 keep-alive 后回落。HTTPS 的成本主要在握手,长连接复用是唯一的解药。

顺带提一个内部服务的小优化。服务间调用我们后来上了 mTLS 双向认证,配合服务发现后内部链路也实现了零信任——即使某台机器被攻破,它的证书也调不通其他服务。这一层投入不大但防御纵深重要,至于是不是要在你们的场景上,看投入产出比,核心链路建议跟。

再加一段关于多域名与泛域名证书的经验。我们的网关后来扩展出了多个业务子域,最初每个子域一张证书,巡检与续期工作量线性增长。统一切到泛域名证书(一张证书覆盖 *.example.com)后管理成本骤降,配合云 SSL 托管续期几乎零操作。泛域名证书的唯一注意点是私钥泄露影响面大,所以它的轮换频率要更高,权限管制更严,仅少数运维同学可接触。选型永远在方便与风险之间找平衡点。

HTTPS 与性能的账

最后算笔性能账,给犹豫加密会拖垮性能的同学一颗定心丸。我们实测开 HTTPS 前后对比:单请求平均耗时增加约 1.5 毫秒(主要是握手与加密开销),但开启长连接复用与 HTTP/2 之后这项摊薄到 0.3 毫秒以下,而服务器 CPU 增加不到一成。相比之下,接口被中间人篡改或凭据泄露一次的代价是百万级起步。这笔账没有任何悬念,全站 HTTPS 无需犹豫。

CDN 侧(文档入口 cloud.tencent.com/document/product/228)还带了一个意外收益:CDN 节点就近终结 TLS,海外客户的首字节时间降到了本地水平,文档站的跳出率降了四成。加密与加速并不冲突,用对工具还能互相成就。

再补一个迁移期的小技巧。灰度切换 HTTPS 期间,总有少数老客户端不支持跳转(比如某些老旧嵌入式设备),对这些设备我们保留了为期三个月的明文兼容通道,但限速到每秒一次且只放白名单接口,同时 CLS 全量留痕供审计。过渡期既要往前走,也要给确实走不动的人留条路,期限公开透明,到期自然关停。灰度与兼容的艺术,说到底是时间管理:窗口给够,底线守牢。

最后给正在纠结要不要全站 HTTPS 的团队一剂强心针。我们核算过:全站 HTTPS 加证书托管加巡检的全部一次性投入约两人周,后续维护每周半小时;而一次凭据泄露事件的平均处置成本是两到三人天加客户信任折损。前者是确定性的小投入,后者是不确定性的大风险,但凡会算账的团队都知道怎么选。加密唯一真正的成本,是你以为它很贵的误解。当你的 HTTPS 也做到全自动巡检的时候,它就成了不会被想起却永远在岗的保镖。这部分投入起步早、节奏稳,后面的高可用篇会感谢你现在打下的底子。证书、跳转、巡检这套组合拳打完,接入层的最后一环也闭合了。

小结

HTTPS 这节的核心其实不是技术,是运营:证书集中托管加自动告警、HSTS 开启但区分内外网、fullchain 配齐全、握手用长连接摊薄。全站加密是网关给客户的第一份承诺,自己先要做到,后来的客户信任评级里这一项加了分。又一次印证那条朴素的经验:安全不是一次配置,是一套持续运行的流程。巡检清单坚持到今天两年多,再没让客户提醒过我们证书的事。

再送一句给小团队的话:没有预算上云 SSL 托管,就用免费的证书加一个 cron 每天扫描到期天数,提前三十天发企业微信告警。零成本也能跑通自动提醒,缺的从来不是工具,是把证书当回事的态度。

还有一个免费的红利:把 HTTPS 与 HTTP/2 一起开启。网关是长连接密集型场景,HTTP/2 的多路复用让我们的连接数降了四成,握手开销摊薄后 P99 还稳了一截。做法是在 Nginx 层终结 HTTP/2,后端仍走 HTTP/1.1,几乎零成本拿到收益。

延伸阅读