NTS: Authenticated Time at Meta
TL;DR · AI 摘要
Meta通过NTS协议实现认证时间服务,提升时间同步的安全性与准确性,已开源相关技术。
核心要点
- NTS使用RFC 8915标准实现时间包认证,防止中间篡改
- Meta时间服务器无状态设计,通过派生密钥替代存储
- 证书验证依赖准确时间,NTS可解决时间同步安全漏洞
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- NTS认证时间服务
- 技术背景
- NTP协议缺陷
- 时间验证安全需求
- 实现方案
- RFC 8915标准
- 无状态服务器
- 密钥派生机制
- 行业影响
- 证书验证安全
- 开源推动采用
金句 / Highlights
值得收藏与分享的关键句。
NTP协议自1985年缺乏认证,但却是所有证书/签名验证的基础
时间误差会导致证书验证失败,如notBefore/notAfter比对错误
Meta时间服务器无状态设计,Cookie密钥通过算法派生而非存储
TLS证书有效期从398天缩短至47天,加剧时间同步安全需求
NTS:Meta的认证时间 - Meta的工程实践
发布于
2026年10月6日
至
网络与流量
,
开源
生产工程
NTS:Meta的认证时间
.entry-meta
.entry-header
作者
Oleg Obleukhov
- Meta的公共时间服务现已通过nts.meta.com支持NTS(网络时间安全,RFC 8915)。数据包经过认证,设备可以验证时间确实来自我们且未在传输过程中被篡改。
- 我们的NTS服务器不保存任何客户端状态。Cookie密钥通过算法生成,既不存储也不复制。
- 我们已通过GitHub上的Meta时间库开源了所有内容,包括协议、服务器和客户端。
- 我们也鼓励所有人,特别是维护Android或iOS系统NTP客户端的开发者,加入我们共同实现NTS支持。
我们之前曾撰文介绍过Meta如何构建更精确的时间服务——从ntpd迁移到chrony,将时间精度从10毫秒提升至100微秒,并向所有人开放time.meta.com服务。
那项工作让时间变得精确。这项工作让时间变得可验证。
为什么选择NTS?
自1985年以来,NTP(网络时间协议)一直未实现认证,这与互联网许多基础协议的情况类似。但它也是所有证书、令牌和签名验证所依赖的基准。客户端发送48字节数据,某个实体返回48字节数据,客户端就会接受。整个过程没有签名,也没有身份验证。
这并非NTP独有的问题。互联网许多基础协议最初设计时都假设网络环境是友好的,此后行业一直在进行安全加固。时间服务的特殊性在于,它不是普通的需要安全保护的协议,而是所有其他安全决策的基准输入。
在NTP的大部分历史中,这种特性影响不大,因为时钟误差几秒只是运营上的小问题。但如今情况已不同:
- 证书验证:notBefore和notAfter字段会与本地时钟进行比较。错误的时钟会导致验证失败。
- 令牌和凭证过期:15分钟的有效期声明本质上是关于时钟的。
- 重放窗口:拒绝超过N秒的旧请求需要准确的时间判断。
- 日志关联和排序:两个不一致的主机会产生一个从未发生过的时间线。
验证本身也存在循环依赖。没有当前时间就无法进行X.509证书的notBefore/notAfter验证。实际上你只能禁用证书时间验证,或采用建议的变通方案。
常见的两种变通方案都存在严重问题。第一种是延迟验证直到时钟设置完成,这会让设备在最脆弱的时候失去保护。第二种是引入缓冲时间:当客户端时钟显示11:59:30时,会拒绝12:00:00签发的证书,因此证书颁发机构会提前几分钟设置notBefore字段,验证方则在本地放宽验证窗口。双方都在猜测对方的时钟误差有多大。
当证书有效期长达398天时,这种问题尚可接受。但CA/Browser论坛的SC-081v3投票改变了这一现状。新颁发的公开信任TLS证书有效期上限已从2026年3月的200天缩短至2027年3月的100天,并将在2029年3月进一步缩短至47天——同时域名验证的重复使用周期也将缩短至10天。
在47天后,手动证书管理在任何规模下都变得不可行。续订周期缩短至大约每月一次,且通常在无人值守的情况下通过ACME协议完成。如果主机的时钟存在偏差,自动化续订循环不会触发任何工单。它会按照预设时间表重新签发证书或失败,而无人关注这一过程。
NTS的工作原理
NTS通过分阶段实现,这种分阶段设计正是其可部署性的关键。
阶段1 – 密钥建立(NTS-KE):基于TCP/4460的TLS 1.3协议,通过ALPN协议(ntske/1)进行协商。客户端和服务器协商确定一个AEAD算法,然后直接通过RFC 5705导出器从TLS会话中派生出两个方向会话密钥(C2S和S2C)。服务器生成8个Cookie,通过服务器协商记录告知客户端实际应使用的NTP服务器,并关闭连接。此过程仅执行一次,而非针对每个数据包。
我们协商了三种AEAD算法:AES-SIV-CMAC-256(ID 15),根据RFC 8915必须实现,且最可能是未知客户端请求的算法;AES-SIV-CMAC-512(17);以及AES-128-GCM-SIV(30)。客户端偏好优先。
阶段2 – 认证NTP:通过UDP/123向已公告服务器发送标准NTPv4数据包,包含NTS扩展字段。请求数据包包含唯一标识符、Cookie和认证信息;认证信息覆盖其前面的所有内容,但不加密任何数据。服务器打开Cookie以恢复会话密钥,验证认证信息后,返回包含唯一标识符的响应以及自身的认证信息——后者携带有效载荷:加密后的最新Cookie。
伪造的数据包会因验证失败而被丢弃。我们不发送NTS NAK,因此失败与数据包丢失无法区分,无法用于强制客户端发起新的密钥交换。重放的响应包含错误的唯一标识符,无法匹配任何未完成的请求。由于替换后的Cookie封装在响应认证信息中而非明文传输,观察者每次通信都会看到不同的不透明数据块,无法跟踪客户端在不同网络间的活动。客户端每次使用Cookie一次是该协议的约定——服务器是无状态的,不追踪这些Cookie。
无状态的Cookie机制
Cookie是服务器传递给客户端用于安全存储的服务器状态信息。它包含会话密钥,通过AES-SIV加密,仅服务器能解密。显然的实现方式是将密钥环复制到每台服务器,这会将时间服务转变为分布式状态问题。
我们不复制密钥。每台服务器从共享主密钥和当前日期派生出封装密钥:
key = HKDF-SHA256(master, salt = BE32(day), info = "fbnts-cookie-seal-v1")
自Unix纪元以来,日期以完整的24小时周期计算,而非日历日。因此不存在时区和夏令时问题,所有主机对同一时刻计算出的整数相同。该整数即Cookie的密钥ID,以明文传输。当服务器收到从未见过的Cookie或来自未通信过的主机的Cookie时,会重新派生密钥并解密。我们接受最多两天前和一天后的密钥以应对轮换边界附近的时钟偏差。
结果是没有密钥环、无需复制、无需会话表、无需共享状态,攻击者也无法穷举。这也是上述分阶段设计的关键——KE服务器和它转交的NTP服务器是不同地点的独立机器,它们之间从不交换任何信息。添加服务器只需添加服务器本身。
普通的NTP路径保持不变。响应器仅在存在扩展字段时才会解析它们,因此未经过身份验证的客户端仍会以相同的成本执行与之前相同的代码。
使用NTS
一行 chrony 配置:
pool nts.meta.com nts iburst maxsources 5
nts.meta.com 仅终止密钥交换。在握手过程中,它会在服务器协商记录中通告一个NTP响应器,因此客户端会被引导至 time.meta.com 并发送经过身份验证的NTP数据。您配置的主机并非最终通信的主机。
这种间接性就是为什么使用 pool 而非 server 的原因。单条 server 配置仅建立一个关联和一个响应器,这会形成单点故障——而NTP协议需要多个源以便能够对错误源进行投票排除。pool 会请求 maxsources 个独立关联。每个关联都通过自己的TLS会话运行独立的密钥交换,因此最终会生成不同的会话密钥对:
$ chronyc authdata
名称/IP地址
模式
密钥ID
类型
密钥长度
最后
尝试次数
NAK
Cookie
长度
time1.meta.com
NTS
5
30
128
27
0
8
68
time2.meta.com
7
142
time3.meta.com
10
548
time4.meta.com
1
1177
time5.meta.com
2
五行配置对应五个独立的认证源,仅需一行配置和一个KE端点即可实现。
横向查看:类型30表示协商的AEAD算法AES-128-GCM-SIV,密钥长度为128位会话密钥——chrony将其列为首选,客户端偏好优先。Cookie长度68表示包含两个16字节密钥的36字节信封。Cookie 8表示已持有Cookie,NAK 0表示没有被拒绝的Cookie。
更值得关注的部分不在表格中。每行的会话密钥都来自其独立的TLS握手,仅对相应的关联有效。将它们封装进Cookie的密钥是共享的——即上述推导中相同的纪元日编号,该编号在每个响应器中都一致,内嵌在每个Cookie中且对客户端不可见。这种分离机制使得单次密钥交换可以为客户端提供五个从未互相通信过的不同响应器。
当池中的某个成员完成密钥交换并切换到协商响应器时,释放的KE地址会分配给下一个未解决的成员,依次循环直到 maxsources 个响应器都完成响应。大约20分钟内会陆续建立五个源,每个成员依次完成自己的密钥交换。
NTS 防御攻击能力
与NTP不同,NTS可以阻止中间人(MITM)攻击伪造响应。经典的MITM攻击无需破解任何加密,只需在真实服务器响应前抢先回复客户端即可。如果攻击者倒退时钟,可以让过期证书重新验证或使被吊销的令牌重新生效。任何依赖过期机制的控制都会被重置。如果攻击者快进时钟,所有内容会同时失效。这种攻击方向更难应对,因为超过某个临界点后,故障将无法恢复。
让我们来看一个设备误以为当前是2037年的例子。NTP使用32位字段记录秒数,该字段将在2036年2月发生溢出。当客户端被卡在这一边界之外时,会错误地计算时间修正值——原本用于修正错误时钟的机制反而将其带入更错误的年代。同时设备持有的所有证书均已过期,导致TLS连接失败,因此无法访问任何可能提供帮助的资源,包括其自身的更新服务。能够修复该问题的补丁无法送达,因为送达补丁本身需要依赖正常工作的时钟。
该设备并未损坏,所有组件都按设计正常工作。问题仅仅在于它持有一个使自身脱离网络可达范围的数字。无论等待多久或重启多少次都无法解决这个问题。恢复只能通过工厂重置或前往服务中心处理。当这种情况扩展到整个设备舰队时,一个错误的整数就会演变为物流问题。
现在考虑对发放凭证的主机发起时钟向前攻击。短期凭证被认为安全,因为它们会快速过期,这种短期生命周期原本就是撤销机制的功能。但这一特性完全取决于发放主机的时钟准确性。如果攻击者让主机误以为当前是明年,那么它签发的凭证有效期将超出你设定的时间窗口。
NTS无法解决的问题
延迟攻击。RFC 8915明确指出,攻击者持有你的数据包时,可使你的时钟偏移量达到引入延迟的一半。协议中的任何加密机制都无法检测到这种攻击,因为每个字节都是真实的。认证只能告诉你数据包的发送者是谁,而无法说明数据包在传输过程中花费了多长时间。缓解措施通常包括:多个独立来源、合理性限制和步长限制。
因此,声明的范围是有限的:路径攻击者仍然可以延迟你的数据包并丢弃它们。但他们再也无法伪造数据包内容。
初始化问题。NTS-KE基于TLS实现,而TLS需要正常工作的时钟,这与上述问题形成相同的循环困境。即使RTC(实时时钟)已失效的设备,在首次握手前仍需要获得大致正确的时间。
错误服务器。NTS验证的是传输路径而非答案本身。如果服务器配置正确且认证正确,但时间计算错误,它会带着完美的签名向你提供错误的时间。仍需要通过多数投票、合理性检查和监控来应对这种情况。
单调性问题。经过认证的时间仍然是墙钟时间,而墙钟时间可能倒退——在闰秒调整或任何修正时刻都可能发生。Cloudflare的权威DNS曾在2016年底遇到这个问题:跨闰秒的解析器往返时间测量结果为负值,影响了上游选择并导致Go语言的rand.Int63n()函数出现恐慌。没有任何伪造行为或错误投递发生。如果你的代码测量的是经过时间,请使用单调时钟。
应用情况
RFC 8915《网络时间协议的网络时间安全》于2020年9月作为建议标准发布在IETF标准轨道上。Cloudflare早在2019年6月就已推出time.cloudflare.com服务,当时使用的是NTS的草案版本,并证明了其可扩展性。
目前服务器端的公共NTS列表包含数十个条目,主要由国家计量院和互联网基础设施运营商(如PTB、Netnod和time.nl)组成。我们正在增加一个新条目。
更大的差距体现在客户端。在服务器领域,你期望看到 NTS 支持的地方确实存在实现——chrony 和 ntpsec 都实现了该功能。但在占据互联网绝大多数终端的平台标准时间客户端中,这项功能却完全缺失。
Android 平台的时间同步采用的是基于 UDP/123 的纯文本 SNTP 协议——固定 48 字节的数据包,不支持扩展字段处理,同时结合 NITZ 和 GNSS 机制——因此其中完全没有 NTS 路径。苹果设备则通过 timed 服务与 time.apple.com 同步,据我们所知这里同样没有 NTS 支持。
这两个时间协议栈为数十亿部手机、手表和耳机设置系统时间,而它们全部都依赖未经身份验证的 UDP 数据包。设备厂商今天就可以部署独立的 NTS 客户端——chrony 在 Android 上运行良好——但这只是平台自身应具备功能的权宜之计。
协议标准已经完成,服务器正在部署,客户端实现也已存在。
缺失的唯有移动设备。
与我们共同推动 NTS 发展
nts.meta.com 已经上线并免费开放。相关代码托管在 github.com/facebook/time。
如果你运营公共时间服务,请开启 NTS 支持。如果你维护 NTP 客户端,请实现 RFC 8915 标准。如果你负责移动操作系统的时间协议栈,这正是需要填补的空白——Android 和 iOS 是全球绝大多数设备设置时间的平台,而它们至今仍依赖未经验证的数据包进行同步。
我们投入数年时间提升时间同步的精确度。但缺乏真实性的精确度只能带来一个来源不明的精确数值。现在,我们终于可以同时实现精确性与真实性。
致谢
我们要感谢 Sofia Scalzo、Pablo Mazzini、Vadim Fedorenko、Patrick Cullen 和 Yixin Wei 对本项目的支持。