应用开发者必须了解的TLS早期数据(0RTT)风险
TLS 1.3代表了二十多年大规模传输安全部署经验的结晶。它主要简化和提升了TLS的安全性,可作为TLS 1.2的直接替代方案。然而,协议中的一项新功能对某些现有应用构成了重大安全风险:TLS 0-RTT(又称早期数据)。这种性能优化可能导致未实现自身防重放机制的应用遭受重放攻击。在某些情况下,仅升级TLS依赖就可能引入应用层漏洞。
一个易受攻击的应用程序案例
假设某公司运营一个具有买卖API的平台。由于历史遗留问题,公司使用GET /api/sell/(item)/(qty)
和GET /api/buy/(item)/(qty)
实现这些操作。
当运维团队将TLS基础设施升级到支持TLS 1.3时(通过在CDN上启用、升级TLS硬件卸载设备或更新负载均衡器软件),如果启用了0-RTT功能,上述API将面临任意重放攻击风险。
攻击场景如下:
- 用户登录系统后连接咖啡店WiFi发起买卖请求
- 得益于TLS 1.3 0-RTT,交易无需初始握手往返,节省300ms
- 攻击者捕获该请求并重新发送给服务器
- 与TLS 1.2不同,该请求不会被TLS层拒绝,买卖操作会再次执行
什么是0-RTT?
TLS 0-RTT(零往返时间,官方称为"TLS早期数据")是一种降低TLS连接首字节时间的方法。当客户端和服务器拥有预共享密钥(PSK)时,客户端可以选择使用该密钥加密早期数据,并与ClientHello一起发送。这使得服务器可以在发送自己的ServerHello/EncryptedExtensions/Finished消息后立即响应请求数据,从而节省整个通信往返时间。
安全风险机制
0-RTT的主要安全问题包括:
- 缺乏前向安全性:0-RTT请求使用之前会话的密钥加密,不具备前向安全性
- 重放攻击风险:0-RTT请求无法防止重放攻击
应用层需要从TLS实现获取信息来判断接收的请求是否为0-RTT,然后通过以下方式防御:
- 拒绝非幂等操作上的0-RTT请求
- 使用nonce等直接防重放机制
解决方案
首要措施
- 升级到TLS 1.3,但暂时禁用0-RTT
- 审计应用程序是否存在此类漏洞
- 如果使用CDN进行TLS终止,查阅文档了解他们转发哪些信息
各平台配置指南
CDN环境:
- Cloudflare使用
CF-0RTT-Unique
头,应用需要跟踪该值并在非幂等端点拒绝重复请求
HAProxy:
- 默认不启用0-RTT
- 可通过配置
allow-0rtt
启用,0-RTT请求会带有Early-Data: 1
头 - 可通过返回425状态码拒绝请求
Nginx:
- 使用
ssl_early_data on;
启用 - 需要添加
proxy_set_header Early-Data $ssl_early_data;
将头信息传递给应用
Apache httpd:
- 2.4.37及以上版本支持TLS 1.3,但目前(2019年3月)不支持0-RTT
编程语言支持情况
- Go:1.12支持TLS 1.3,但不支持0-RTT
- Python:目前无早期数据支持
- Ruby:目前无早期数据支持
- C:使用OpenSSL时,需调用
SSL_CTX_set_max_early_data
启用0-RTT,可使用SSL_CTX_set_allow_early_data_cb
设置回调函数
研究进展
密码学家正在研究如何在0-RTT请求中实现可用的前向安全性。最新研究提出使用可穿刺伪随机函数来显著减小会话数据库大小,但这在计算复杂性和后妥协安全性方面存在权衡。目前这仍是一个活跃研究领域,尚无真正适合部署的解决方案。
如果您想在利用TLS 1.3性能优势的同时确保应用程序和用户在0-RTT环境中的安全性,请联系Trail of Bits的工程和密码学团队。