以错误结束的握手,在你读消息之前看起来像攻击;多数时候这是算术问题:没有共同提议、证书过期,或者包根本没到。降级攻击是另一回事:攻击者把两端推向比它们本会选择的更弱的行为,留下的证据也不同。把每次失败都当成恶意,会让你去找一个可能并不存在的受害者;而把真正的降级当成配置错误,则会让更弱的结果留在原地。两种错误的代价不同,只有一种修起来便宜。

降级攻击实际改变了什么

目标不是破解密码,也不是猜出密钥。目标是影响协商,让双方停在一个更老的版本或攻击者能应付的更弱算法上,或者让一方相信对方只支持那个。

变的是约定,不是身份认证。如果攻击者能在不被发现的情况下改动提议,双方仍能完成握手并互相认证,只是用的是攻击者偏好的参数。所以真正有意思的问题是:协商本身是否被完整性保护覆盖。

为什么协商本身必须被认证

TLS 1.3 把这种保护明确下来。提出较新版本的客户端能发现悄悄回退的服务器,因为服务器在协商 1.2 或更低版本时,其随机值带有固定模式,客户端看到就必须中止。完整会话记录也被完成消息覆盖,因此协商中任何位置的改动都会在后面失败,而不是悄悄通过。

更老的协议版本说法更弱,这正是回退规则要紧的原因。同一份配置里的 TLS 1.0 端点不是降级攻击,而是你自己选择允许的、被许可的结果。区别在于更弱的结果来自受保护的决策,还是来自中间人可以影响的未受保护路径。一旦这样表述,大多数事件都会归结为配置问题。

IKEv2 行为不同,但基于同一思路。提议承载在一次交换中,而这次交换随后会认证双方,所以重写算法的中间设备会破坏认证,而不是操纵它。OpenVPN 的控制通道用 TLS 保护其协商,然后用该通道商定数据通道的密码套件,所以控制通道那行日志才值得看。

配置出来的兼容不是攻击

管理员可以为了支持有文档的客户端而故意允许更老的选择,这个决定在配置里看得见。攻击者无法制造这样的配置,只能利用一条让其影响提议的路径。

危险的修复是抹掉差异证据的那种。启用所有旧选项、关闭证书校验,或者为了让错误消失而接受任何密码套件,都会扩大端点愿意同意的范围,并移除本该报告更弱结果的检查。

如果必须保留一个旧选项,就在配置注释里写明原因和客户端,然后把该选项限制在需要它的配置文件里。限定到单个监听器的兼容例外可以审计;全局应用的只是一个没人记得自己选过的更弱默认值。

先读失败,再给原因命名

在做任何改动之前,保存精确的错误字符串、客户端与服务器版本以及时间。然后分类:是双方对提议有分歧、包路径失败,还是一方拒绝了它曾经接受的参数?

提议不匹配通常会点名有问题的版本或算法,并且每次都在同一点失败。路由或 MTU 问题会卡住或超时,而且常常随网络变化。证书过期产生的是校验错误,不是协商错误。

这些都不是攻击者的证据。在第一次失败就归因于恶意,才是错误,因为它掩盖了明天还会再失败的那个确定性缺陷。

确认实际选中的是什么

任何修正之后,去读协商出来的结果,而不是相信连接已经起来了。因为移除了校验而成功的连接,正是你想避免的结果。下面两条命令会显示一个前置 TLS 的端点所选中的版本与密码套件,以及 OpenVPN 服务器日志中的控制通道行。

openssl s_client -connect 198.51.100.30:443 -servername vpn.example.net -brief </dev/null
grep -iE 'Control Channel|Data Channel' /var/log/openvpn.log | tail -5

把结果和你原本想要的配置比较,而不是和现在能连上的东西比较。如果选中的版本比允许的最低版本更老,该看的是配置文件或回退列表,答案是修正那份列表,而不是宣布遭到攻击、把周围一切都重建。

相关文章