“这个错误的修复很简单:检查消息的长度是否实际上与传入请求的长度相匹配。”
为什么我们甚至让客户报告长度?
如果我们可以知道传入请求的长度,我们就不能从中推断出消息的长度吗?
(这是一个编程和协议设计问题。)
“这个错误的修复很简单:检查消息的长度是否实际上与传入请求的长度相匹配。”
为什么我们甚至让客户报告长度?
如果我们可以知道传入请求的长度,我们就不能从中推断出消息的长度吗?
(这是一个编程和协议设计问题。)
我不知道有关此主题的任何明确的“官方”答案,但这似乎是通用性和连贯性尝试的一部分。在SSL/TLS标准中,所有消息都遵循常规编码规则,使用特定的表示语言。协议的任何部分都没有从记录长度“推断”长度。
一个启发性的细节是ClientKeyExchange
消息:在SSL 3.0中,使用 RSA 密钥交换,握手消息的内容是“原始”加密结果(即,如果服务器使用 2048 位 RSA 密钥,则为 256 个字节,加上 4 个字节握手消息头);在TLS 1.0和后续版本中,加密结果是一个“不透明向量”,带有一个包含长度的额外 2 字节标头,因此它是 258 字节,而不是 256 字节,并且仍然带有额外的 4 字节握手消息标头。这一变化说明了标准设计者希望通过尽可能少的数据包长度推断来实现严格的规律性。
不从外部结构推断长度的一个好处是它可以更容易地在之后包含可选扩展。例如,这是通过ClientHello
消息完成的。
如果我们从更高的角度来看事情,TLS 只是完成了一半。TLS作为一种协议,需要交换结构化数据;发送方和接收方必须能够毫无歧义地对此类数据进行编码和解码,最好也没有缓冲区溢出。一种策略是执行以下操作:
该策略的一个实施例是ASN.1。例如,在 X.509 证书中使用 ASN.1;使用 ASN.1 编译器,您可以(理论上)粘贴X.509 标准中的 ASN.1 语法并获得 X.509 证书的完整编码器/解码器引擎(我已经这样做了,包括编写我自己的 ASN.1编译器,并且有一些“怪癖”,包括字符串和日期的 ASN.1 类型似乎是由 LSD 上的狒狒发明的;但总体方案仍然有效)。
另一个实施例是XML,其中XML Schema作为“表示语言”:您将模式和输入数据推送到 XML 解析器中,如果生成的不是错误代码,那么您知道所有数据都符合模式,你可以在一个解码的、结构化的树中遍历它。同样,这就是理论,就理论而言,这很好。
TLS 的“表示语言”在这里有点短:它没有编译器。这种语言看起来像一种“计算机语言”,但它是为人类阅读而设计的。为此,它运作良好;但是,这意味着开发人员必须将该语言转录到编码器/解码器中。开发人员必须注意不要忘记一个步骤。这正是“heartbleed”错误所发生的情况:忘记了一个步骤,这可能会产生可怕的后果。
对于以活力(保持活动)检查为目的的 TLS,没有理由:
rrec.length
OpenSSL 代码中的记录层——您只需从中减去固定的 HB 标头大小),因此,与普通 TLS 相比,该设计存在缺陷且过于复杂。注意 TLS 是我们真正关心的广泛使用的协议,它加密所有 HTTPS 流量。
在易受攻击的 OpenSSL 提交中,所有生成的 Heartbeat 请求都有一个小的固定有效负载(18 个字节),并且在处理接收到的 HB 响应时,OpenSSL 仅检查包含 HB 序列号的前两个字节。来源:t1_lib.c(包含所有 TLS HB 代码)在生成 HB(仅在 中描述)时,它将有效负载大小固定为 18。在tls1_process_heartbeattls1_heartbeat
中处理 HB 响应也仅在有效负载恰好为 18 时才进行任何有意义的处理。注意TLS中的请求处理是破坏 HTTPS 的易受攻击的部分。
背景
在谈到所声称的理由之前,我必须介绍三个概念:DTLS、PMTU 和 PMTU 发现,它们都与活力检查无关,但要处理 Heartbeat 扩展的其他建议用途。如果您熟悉,请跳至建议的理由。
TLS(TCP 加密)和 DTLS(UDP 加密)
常规 TLS 在 TCP 之上添加加密。TCP 是一种提供可靠传输流的传输层协议,这意味着应用程序接收到重构的数据流,所有数据包都以原始顺序呈现给应用程序,一旦一切就绪,即使有些必须等待一些额外的时间才能收到数据包怨恨。TCP 还提供拥塞控制(如果由于拥塞而丢弃数据包,TCP 将调整数据包的发送速率)。所有 HTTP、HTTPS、SFTP 流量都通过 TCP 发送。
数据报 TLS (DTLS) 是一种较新的协议,它在 UDP 之上添加了加密(以及类似的数据报协议,如应用程序可以完全控制如何发送数据包的 DCCP)。这些是不提供可靠流的传输层协议,而是在应用程序控制的客户端/服务器应用程序之间直接发送数据包。使用 TCP 如果数据包丢失,它会自动重新发送并延迟发送进一步的数据包,直到丢失的数据包通过。UDP 为应用程序提供了数据包级别的控制,这对于双向视频聊天等实时通信通常是可取的。如果数据包 A、B、C、D 已发送,但数据包 C 丢失,则在向用户显示数据包 D 之前等待 C 重新发送是没有意义的——这会导致长时间的停顿。
PMTU
对于 DTLS,希望知道路径最大传输单元. 路由器之间单个链路的 MTU 是可以发送的最大数据包大小。不同的路由器和链路类型通常支持不同的 MTU。路径 MTU(数据包通过网络的路径上的最小 MTU)通常不会事先知道它是通过网络的路径的属性。如果您发送大于 PMTU 的数据报,它们将不得不在最小的 MTU 点进行分段,这有几个原因是不可取的(防火墙/NAT 可能会丢弃低效的分段数据包,这会使应用层感到困惑,而 ipv6 会被设计永远不会对数据包进行分段)。因此,在 DTLS 的上下文中,RFC 强制来自记录层的数据适合单个 DTLS 数据包(小于 PMTU)。(使用 TLS,这些 PMTU 问题在 TCP 级别处理;
PMTU 发现
有一些协议可以发现 PMTU——特别是分组层路径 MTU 发现 (RFC 4821)。在这种情况下,您通过发送各种大小的数据包(配置为不分段)来探测网络,并根据您的数据包是否通过网络来跟踪 PMTU 的上限和下限。这在 RFC4821 中有描述。如果探测数据包通过,则提高下限,如果丢失,则降低上限,直到上限/下限接近并且您获得估计的 PMTU,该 PMTU 用于设置 DTLS 数据包的上限大小。
声称 HB 具有有效负载标头、填充、具有最多 2 字节大小的字段的理由
heartbeats RFC RFC6520说您可以使用 Heartbeats 进行 DTLS 的路径 MTU 发现:
5.1。路径 MTU 发现
DTLS 执行路径 MTU 发现,如 [RFC6347] 的第 4.1.1.1 节所述。[RFC4821] 中给出了如何执行路径 MTU 发现的详细描述。必要的探测包是 HeartbeatRequest 消息。
DTLS 应用程序确实需要估计 PMTU。但是,这不是由 DTLS 完成的,而是由使用 DTLS 的应用程序完成的。查看 RFC6347 的 DTLS RFC第 4.1.1.1节的引用部分,它指出“通常,DTLS 的理念是将 PMTU 发现留给应用程序。” 它继续为 DTLS 必须担心 PMTU 的原因提供三个警告(DTLS 应用程序必须减去 DTLS 标头以获得数据的有效 PMTU 大小,DTLS 可能必须将 ICMP“数据报太大”传回应用程序层,以及 DTLS握手应该小于 PMTU。在 DTLS RFC 的早些时候,它声明 DTLS 记录必须适合小于 PMTU 的单个数据报,因此 PMTU 发现/估计必须由应用程序使用 DTLS 完成。
在 PMTU 发现中,有一个描述有效负载长度的小字段是有意义的,有大量的任意填充,并且有一些回显的东西,是的,即使我只是把你发回给你,我也会用这个大小的 MTU 得到你的请求序列号(为了提高效率,可以删除响应中的填充)。当然,如果您描述有效负载的大小以允许有效负载大于约 4-32 个字节,那么有效负载大小可以固定或由一个字节字段描述,即使任意长的填充可以被串联。
索赔分析
HB RFC 中的 OpenSSL HB 实现和描述,没有描述或执行这个 PMTU 发现协议。代码中不存在 MTU。OpenSSL 确实只提供了一种机制来在 TLS 和 DTLS 中生成 HB 请求,但它的大小是固定的(18 字节有效负载,不可配置)。
充其量,这是一个包含 YAGNI 功能的严重缺陷设计(在 TLS 中),然后将其编码为完全信任用户提供的标头字段而无需任何健全性测试。在最坏的情况下,PMTU 部分只是一个复杂的封面故事,允许插入易受攻击的代码,从而提供一些看似正当的理由。
搜索 IETF TLS 邮件列表
如果您搜索IETF TLS 邮件列表,您会发现有趣的掘金。为什么有效载荷/填充长度是 uint16,如果要丢弃它,为什么会有填充? PMTU 发现。同一个提问者 (Juho Vähä-Herttua) 表示他更喜欢数据包验证:读取有效负载长度、填充长度,并验证它是否匹配记录长度(减去标头)。还有西蒙约瑟夫森:
我对允许任意有效载荷有一个轻微的担忧。这样做的理由是什么?它为 TLS 中的侧通道打开了大门。它也可能被滥用来发送非标准化数据。此外,是否有任何理由允许任意大小的有效载荷?在我看来,payload_length、payload 和 padding 字段对我来说似乎是不必要的。
Michael Tüxen 的回应在很大程度上是不够的(也许想在上面添加一些功能来表示计算 RTT)并总结为“这里的重点是,对于互操作性,有效载荷是什么并不重要,重要的是它被反映。”
随机填充的另一个值得注意的原因是“[我们]随机化......心跳消息中的数据以尝试解决由弱密码或有缺陷的密码引起的任何问题。”然后是问题“是否有任何论文或密码文档讨论如何使用数据包中的随机数据可以解决未来可能出现的密码缺陷吗?”,然后是一篇关于确定性认证加密的论文。反响很好:
确实,但这不是像 CBC 或 CTR 这样的通用加密模式。它专门设计用于加密随机密钥,因此取决于其随机性。典型的加密模式专门设计用于防止有人将给定的明文加密与随机加密区分开来。
现在,如果想在 TLS 中使用潜意识通道,心跳扩展现在提供了一个无界通道。
有趣的是,该评论从未得到解决。(除了其他人评论“嗯,这是一个完全不同的问题......”)。
如果您查看RFC6520(心跳扩展),则有效负载后有一个填充。所以需要知道有效载荷在哪里结束和填充开始的长度。除此之外,我发现设计过度设计:此扩展的两个原因似乎是使 PMTU 成为可能(通过使用不同大小的消息)和通过心跳知道另一端是否还活着或重置一些超时计数器中间盒的状态。无需在这些场景中反映用户指定的有效负载。
从安全的角度来看,这些过度设计的协议,你只测试你真正需要的东西,只是乞求未来的麻烦。