1398 字
7 分钟
全节点挂了先查时间:代理协议为什么对时钟敏感

起因是群晖 Docker 里跑 mihomo,节点全挂;同样的配置文件电脑上一切正常。

排查了一圈 DNS、network mode、TUN 权限、IPv6,最后发现是容器时间和真实时间差了一两分钟。再往上追,是群晖自己的 NTP 同步早就失败了——DSM 同步失败默认不会有任何提示,时间就这么悄悄漂了下去。

把 NTP 服务器换成 ntp.aliyun.com,顺手加了个每日强制同步的计划任务,容器时间自动跟上,节点立刻全部恢复。

这件事把”代理协议为什么对时间这么敏感”这个老问题翻了出来,整理一下。

防重放是这条思路的起点#

攻击者抓取一个合法的加密数据包(比如代理客户端的握手请求),原样重新发送给服务器。即便解不开内容,只要服务器接受了这个包,就能:

  • 被动监听抓包后重放,如果服务器有响应(哪怕只是行为上的差异),就能确认这是个代理节点,进而被封;
  • 批量重放消耗服务端连接和算力;
  • 某些弱协议里可以直接伪造身份建连。

防御核心:让每个握手包只能用一次——需要一个双方都能独立产生、又能相互验证的”一次性凭据”(nonce)。

VMess:把时间戳当 nonce 用#

VMess 是这套思路的典型代表。客户端发起连接时,会用 用户 UUID + 当前时间戳(分钟级) 经过 HMAC / 哈希算出一个验证字段,塞进请求头。服务端收到后:

  1. 用同样的 UUID 和自己当前的时间算一遍;
  2. 因为允许偏差,服务端会在 [now-90s, now+90s] 这个窗口里每个时间点都试一次,任意一个匹配就认证通过;
  3. 同时把 (时间戳, hash) 记到一个短期缓存里,同一个组合在窗口期内只接受一次

效果:

  • 抓到完整握手包,过 2 分钟再发,服务端拒绝(窗口外);
  • 在窗口内重放,会被去重缓存挡掉;
  • 服务端不需要给每个客户端维护 nonce 状态,省内存。

时间戳本质上是一个廉价的、双方都能独立产生的一次性 nonce。 代价也很明确:客户端和服务端时钟必须同步,差几分钟就全挂。

三代协议,对时间戳的态度逐步松绑#

第一代是 VMess、Shadowsocks AEAD-2022、Hysteria、TUIC 这一批,自己造防重放、强依赖时间戳。2018 年前后 TLS 1.3 还没普及,协议必须自己解决重放问题,时间戳是当时最简单、不需要双方维护状态的方案。代价就是时钟一漂全节点不可用,把运维负担转嫁给了用户。

第二代是 Trojan,把防重放交给外层 TLS。协议本身只是一个简单的密码验证,外面套标准 TLS,重放由 TLS 1.3 自带的 anti-replay 机制(PSK + ticket nonce)兜底。协议层不再敏感,只有底层 TLS 证书校验间接需要时间大致正确——差几分钟无所谓,差几年握不了手。

第三代是 VLESS + Reality,彻底把时间戳扔掉。VLESS 协议层砍掉所有加密和时间戳验证,安全完全交给外层传输;Reality 又换了一种鉴权方式:客户端”借用”一个真实知名网站的 TLS 证书做伪装,鉴权通过 X25519 密钥交换完成,根本不需要时间戳。对时间完全不敏感,抗审查能力还更强(流量看起来就像在访问真实网站)。

关于 VMess 这套设计的几点质疑#

放到现在看,时间戳防重放有不少能挑的地方:

  • 现代 TLS 1.3 本身就带 anti-replay,底层加密通道已经防住大部分重放,应用层再做一遍有点叠床架屋;
  • 真实世界里,因时钟不同步导致的”误伤”远多于实际被防住的攻击。一台没同步 NTP 的 NAS、一个休眠后没对时的笔记本、一个虚拟机宿主漂了时间,立刻全节点不可用;
  • 把安全机制建立在”用户能维护好时钟”这个假设上,本身就是脆弱的。

可以这么理解:VMess 时代自己造一层防重放是合理的,在今天看更像是历史包袱

留给自己的几条实践#

  • NAS、软路由、自建服务器这类”基础设施型”机器,NTP 一定要配好且配国内源(ntp.aliyun.com / ntp.ntsc.ac.cn)。受影响的不只是代理,证书、日志、备份、定时任务都会有问题。
  • 群晖、OpenWrt 这种设备,默认 NTP 同步失败不会提示,手动加一个每日强制同步的计划任务兜底。
  • 长期使用 mihomo / sing-box,优先选 VLESS + Reality 或 Trojan + TLS 节点,对时间不敏感,运维省心。VMess 节点留作备份即可。
  • 遇到”全节点挂了”,排查顺序:时间 → DNS → 网络出站策略 → 节点本身

一句话收尾:时间戳防重放是 TLS 1.3 普及前的过渡方案,用时钟同步换简单;现代协议把这层防御下沉到传输层,让协议层不再背时间这个包袱。“全节点挂了先查时间”是 VMess 时代留下的运维直觉,等都迁到 VLESS + Reality 后,这个直觉也就用不上了。

全节点挂了先查时间:代理协议为什么对时钟敏感
https://blog.cuixu.cn/posts/proxy-protocol-time-sensitivity/
作者
崔旭
发布于
2026-05-15
许可协议
CC BY-NC-SA 4.0