好好的证书被拒绝了
一句话总结
证书、令牌和缓存,都要把“现在几点”与对方核对。如果自己的时钟不准,正常的东西会被拒绝,已过期的东西反而能通过。
为什么需要它
大多数时钟误差不会引发任何问题。日志差几秒,只是读起来不方便。但在判定期限的地方,同样的误差会直接变成拒绝。而且这个拒绝并不会指明原因。消息是“证书尚未生效”,而证书其实是刚拿到的正常证书。
尤其让人困惑的是它会自行好转。时钟落后 12 秒的机器,拿到新证书后只会失败 12 秒,之后一切正常。于是只在部署刚结束时出现几个错误,而且无法复现。这类问题通常被归结为“临时的网络问题”,并在下一次部署时原样重演。
工作原理
X.509 证书用两个时间点来表示有效区间:notBefore 和 notAfter。RFC 5280 第 4.1.2.5 节把这个区间定义为“从 notBefore 到 notAfter,包含两端”。验证方用自己的时钟检查现在是否处于这个区间内。因此,同一张证书在某些机器上能通过,在另一些机器上却被拒绝。
openssl x509 -in edge-1.pem -noout -startdate -enddate
# notBefore=Nov 1 09:38:00 2025 GMT
# notAfter=Feb 1 09:38:00 2026 GMT
时钟落后的机器会卡在前端。新签发证书的 notBefore 比这台机器的“现在”更靠后,它就判定证书尚未生效。耗费的时间恰好就是落后的量。反过来,时钟超前的机器会卡在后端,比别人更早地认为证书已过期。两种症状方向相反,知道是哪一种,也就立刻知道时钟是往哪个方向错的。
令牌的结构也是一样。RFC 7519 中的 exp 声明(claim)规定“在该时间及之后(on or after)不得接受”,而 nbf 规定“在该时间之前不得接受”。并且两节都附加了同一句话:实现者可以考虑时钟误差,留出少许余量(leeway),通常不超过几分钟。
寿命与误差的关系在这里显现出来。如果由一台时钟超前 277 秒的机器来验证寿命为 300 秒的令牌,从签发 23 秒之后起,它就看起来已经过期了。误差越接近寿命,可用时间就越趋近于 0。所以寿命越短,对时钟精度的要求就越苛刻。如果决定使用 1 分钟的令牌,就必须先确定时钟误差预算。
쓸 수 있는 시간 = 수명 - (검증하는 쪽 시계가 앞선 만큼)
수명 300초 · 오차 +277초 → 23초
수명 300초 · 오차 + 5초 → 295초
수명 60초 · 오차 +277초 → 아예 못 쓴다
该代码块中的韩文说明依次为:可用时间 = 寿命 − 验证方时钟超前的量;寿命 300 秒、误差 +277 秒时为 23 秒;寿命 300 秒、误差 +5 秒时为 295 秒;寿命 60 秒、误差 +277 秒时完全无法使用。
缓存也处于同一条轴上。如果响应中附带的过期时间以绝对时间给出,就取决于接收方的时钟;如果以剩余秒数给出,接收方用自己的单调时钟计数,就不会受误差影响。HTTP 除了 Expires 头之外,另设了 Cache-Control: max-age 并让后者优先,原因就在这里。
在现场相遇的样子
在有自动续期的环境中,这个问题会与部署周期交织在一起。与其在证书临近过期时才更换,不如留出充足的余量,提前更换,这样即使有几分钟的时钟误差,也不会卡在两端的任何一端。把续期时间定在有效期三分之二处左右的惯例,就是由此而来。
排查认证失败时,顺序也是固定的。先看消息是“not yet valid”还是“expired”。如果是前者,说明验证方的时钟落后了,或者是刚刚完成签发;如果是后者,说明验证方的时钟超前了,或者证书确实过期了。然后再直接比较两边机器的时间。这两步就能理清大多数事件,但实际上人们往往先去重新签发证书。于是同样的事情在下一次续期时再次发生。
也可以靠把余量(leeway)设得很大来掩盖问题,但这是有代价的:它意味着要把已过期的令牌再多接受这么久。如果设计中用重新签发来代替撤销,撤销就会相应地晚生效这么久。余量只应作为在修好时钟之前的临时措施,根本的对策是测量误差并加以监控。
还有一点。由期限引起的失败,两端的症状并不对称。前端问题(尚未生效)在签发后短暂出现就会自行好转,而后端问题(过早过期)一旦开始,就会一直持续到修好时钟或续期证书为止。因此前者会被报告为“无法复现的零星错误”,后者则被报告为“突然全部挂了”。原因相同,报告的标题却截然相反。接到事件时,如果先想起这种不对称,就能立刻决定该看哪一端。
而且,判定期限的地方在代码中很容易找到。凡是在某处读取“现在”,并与别人给出的时间相比较的地方,都是这样的地方。证书验证、令牌验证、缓存新鲜度、签名 URL 的过期、防止重放攻击的时间戳窗口,形态全都相同。只要把这些地方列成一张清单,时钟误差预算应当定为多少,就不再是猜测,而是计算——清单中最短的寿命决定了预算的上限。
下一项测验要确认什么
确认时钟落后的机器和时钟超前的机器分别会卡在有效区间的哪一端,令牌寿命与时钟误差如何相乘从而缩短可用时间,以及增大余量这种对策的代价是什么。