“此错误的解决方法很简单:检查消息的长度是否确实与传入请求的长度匹配。”

为什么我们什至让客户完全报告该长度?

如果我们知道传入请求的长度,就不能仅从中推断出消息的长度吗?

(这是编程和协议设计的问题。)

评论

你撞到了头。长度是多余的信息,并且是该错误存在的唯一原因。

#1 楼

对于出于活动性(保持活动)检查目的的TLS,没有理由:


在心跳请求/响应标头中编码有效载荷大小字段(有效载荷的长度从OpenSSL代码的记录层rrec.length中-您只需要从中减去固定的HB标头大小,
允许HBs大小可变-较小的HB大小(在约4-32范围内)位元组(bytes)即可正常运作-仅需序列号即可,
在有效负载中添加填充,或
执行PMTU发现(定义如下)

因此设计存在缺陷且过于复杂关于普通TLS。注意TLS是我们真正关心的广泛使用的协议,用于加密所有HTTPS通信。

在易受攻击的OpenSSL提交中,所有生成的Heartbeat请求都具有较小的固定有效负载(18个字节),并且在处理收到的HB响应时,OpenSSL仅检查它的前两个字节,其中包含HB序列号。来源:t1_lib.c(包含所有TLS HB代码)在生成HB时(仅在tls1_heartbeat中进行了描述),将有效负载大小固定为18。如果有效负载恰好为18,则在tls1_process_heartbeat中处理HB响应也仅进行任何有意义的处理。请注意,在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级别处理的;不是在应用程序层处理的,因此您的TLS可以与PMTU无关)。

PMTU发现

有一些协议可以发现PMTU-具体来说是打包层路径MTU发现(RFC 4821)。在这种情况下,您可以通过发送各种大小的数据包(配置为不分段)来探测网络,并根据数据包是否通过网络来跟踪PMTU的上限和下限。 RFC4821中对此进行了描述。如果探测数据包通过,则提高下限,如果丢失,则降低上限,直到上限/下限接近,并且您具有估计的PMTU,该PMTU用于设置DTLS数据包的上限。


具有有效负载标头,填充,最大2字节大小字段的HB的合理理由

心跳RFC RFC6520表示您可以将心跳用于路径MTU DTLS发现:


5.1。路径MTU发现

DTLS按照
第4.1.1.1节中的描述执行路径MTU发现[RFC6347]。 [RFC4821]中详细说明了如何执行路径MTU
发现。必需的探测数据包是
HeartbeatRequest消息。


DTLS应用程序确实需要估计PMTU。但是,这不是由DTLS完成的,而是由应用程序使用DTLS完成的。查看DTLS RFC RFC 6347的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字节,则毫无意义,因此有效负载大小可以由一个或多个固定或描述即使可以串联任意长的填充。

对Claim

的分析和HB RFC中的OpenSSL HB实现,并没有描述或执行此PMTU发现协议。代码中不存在MTU。 OpenSSL确实仅提供一种在TLS和DTLS中生成HB请求的机制,但是它具有固定的大小(18字节有效负载,不可配置)。


没有功能可以发送一系列HB来探测OpenSSL代码中的PMTU发现,也无法详细描述在探测过程中如何使用HB,
没有迹象表明HB被配置为不分段(因此它们可以甚至可以通过这种方式使用),
如果您想使用HB进行PMTU发现,则应用程序编写者必须自己为客户端和服务器编写所有代码。
即使在找到PMTU的情况下,有效载荷字段也只需要一个字节(即使它不是固定的)。
没有理由让他们不做整个探测过程来使用HB数据包。客户/服务器应用程序中的任意类型的数据包;例如,使用普通的UDP数据包。
PMTU发现仅在DTLS上下文中才有意义,那么为什么在TLS心跳中会出现这些完全不必要的功能-不需要知道PMTU的应用程序?

充其量这是一个带有YAGNI功能的严重缺陷(在TLS中)设计,然后对其进行了糟糕的编码,以完全信任用户提供的标头字段,而没有进行任何健全性测试。最糟糕的是,PMTU部分只是一个复杂的封面故事,它允许插入易受攻击的代码,从而提供一些正当的理由。


在IETF TLS邮件列表中进行搜索

如果您搜索IETF TLS邮件列表,则会发现有趣的块。为什么有效载荷/填充长度为uint16,为什么填充会被丢弃? PMTU发现。相同的询问者(JuhoVähä-Herttua)指出,他非常希望数据包验证:读取有效载荷长度,填充长度,并验证其是否与记录长度匹配(减去报头)。另外,西蒙·约瑟夫森(Simon Josefsson):


我对允许任意有效载荷有一点关注。
这样做的理由是什么?它为TLS中的辅助通道打开。
也可能被滥用来发送非标准化数据。此外,有没有
为什么允许任意大小的有效载荷?在我看来,
payload_length,payload和padding字段对我来说似乎是不必要的。


MichaelTüxen的响应很不充分(也许想在顶部添加一些功能来说明)计算RTT),并总结为“这里的重点是,对于互操作性,有效负载是什么都无关紧要,
反映是非常重要的。”

还要注意随机填充的原因“ [我们]将心跳消息中的数据随机化,以尝试处理由于弱密码或有缺陷的密码而引起的任何问题。”后跟问题“是否有任何论文或密码文档讨论了如何使用数据包中的随机数据会解决将来可能出现的密码缺陷吗?”,然后发表有关确定性认证加密的论文。响应很好:


的确如此,但这不是CBC或CTR这样的通用加密模式。它
是专门设计用于加密随机密钥的,因此取决于
它的随机性。典型的加密模式专门用于
防止某人将给定的明文加密与随机的
区分。

现在,如果要在TLS中使用潜意识通道,则
心跳扩展现在提供了无限制的渠道。


有趣的是,该评论从未得到解决。 (除了其他人评论“嗯,这是一个完全不同的问题...。”)。

评论


是的,很乱。我想知道您是否可以考虑要求响应包含填充的合理理由?这是草案v4(tools.ietf.org/rfcdiff?url2=draft-ietf-tls-dtls-heartbeat-04)引起的更改,我认为该版本是IESG反馈的结果。与“扩展随机” TLS草案(projectbullrun.org/dual-ec/ext-rand.html)的情况进行比较可能会提供很多信息。

– Peter Dettman
14年4月14日在4:47

@PeterDettman-我在TLS IETF邮件列表中添加了另一段很长的块。有趣的是,许多人指出这潜在地打开了旁道攻击。根据对DAE的分析,添加随机填充似乎是不合理的,并且模糊的希望也可以防止不良密码的缺陷(同时让我们打开这个辅助通道)。

– jimbob博士
2014年4月14日下午5:49

的确,最近几天我已经很熟悉列表讨论了。较小的编辑:“为什么填充长度​​为uint16”应引用有效负载长度?

– Peter Dettman
14年4月14日在7:01

@PeterDettman-那时标准说只是转储有效负载和填充-没有指定如何区分有效负载/填充(大概在那个时候头没有提到)。 Juho首先说让我们指定标头(2),并担心为什么允许这些字段很大(与这两种类型有关),如果允许2 ^ 14-5 = 16379需要一个uint16(两个字节的字段) 。

– jimbob博士
14年4月14日在7:15

嗯,这是在较早版本的上下文中理解的。

– Peter Dettman
14年4月14日在9:59

#2 楼

我不知道有关此主题的任何明确的“官方”答案,但这似乎是通用性和连贯性尝试的一部分。在SSL / TLS标准中,所有消息都使用特定的表示语言遵循常规的编码规则。协议的任何部分都不从记录长度“推断”长度。

一个令人鼓舞的细节是ClientKeyExchange消息:在SSL 3.0中,通过RSA密钥交换,该握手消息的内容是“原始” “加密结果(即,如果服务器使用2048位RSA密钥,则为256字节,外加4字节的握手消息头);在TLS 1.0和后续版本中,加密结果是“不透明矢量”,带有一个包含长度的额外两字节标头,因此是258个字节,而不是256个字节,并且还带有一个额外的4字节握手消息标头。此项更改说明了标准设计人员的意愿,即通过尽可能少地从数据包长度得出推断来实现严格的规则性。

不从外部结构推断出长度的一个好处是,它可以更容易地包括在内之后是可选扩展。例如,这是通过ClientHello消息完成的。


如果从更高的角度来看事情,TLS仅完成了一半。 TLS作为协议,需要交换结构化数据。发送方和接收方必须能够无歧义地编码和解码此类数据,并且最好也不会出现缓冲区溢出。一种策略是执行以下操作:


为结构化元素定义严格的常规编码规则。例如,定义每个元素之前都应包含一个包含元素长度的标头。
定义明确的语言来表示所有消息的结构和类型,并将其作为协议标准的一部分;这是RFC的“表示语言”。
编写一个通用的编译器软件,在其中粘贴RFC中的表示语言,并生成一个编码器/解码器实现,该实现可处理所有消息的编码和解码,并对所有长度和值范围进行系统检查,等等。 />
此策略的一个实施例是ASN.1。 ASN.1例如在X.509证书中使用;使用ASN.1编译器,您可以(从理论上)粘贴X.509标准中的ASN.1语法,并获得用于X.509证书的完整编码器/解码器引擎(我已经做到了,包括编写自己的ASN.1编译器,并且存在一些“怪癖”,包括ASN.1类型的字符串和日期似乎是LSD上的狒狒发明的;但是总体方案仍然有效。

另一个实施例是XML,其中XML架构为“表示语言”:将架构和输入数据推送到XML解析器中,如果生成了错误代码以外的内容,那么您知道所有数据都符合该架构,您可以将其放入经过解码的结构化树中。再说一遍,就理论而言,这就是很好的理论。

TLS的“表示语言”在这里有点不足:它没有编译器。该语言看起来像“计算机语言”,但它是供人类阅读的。为此,它运作良好;但是,这意味着开发人员将不得不将该语言转录为编码器/解码器。开发人员必须注意不要忘记一步。而这正是发生在“ heartbleed”错误中的情况:忘记了一个步骤,这可能会带来可怕的后果。

评论


我想如果Netscape在大约5至8年后才发明SSL,那么所有内容都将以XML而不是ASN.1编码。

– tylerl
2014年4月13日在19:16

不用担心,ASN.1和XML催生了一个混蛋:XER(用于“ XML编码规则”)。

–托马斯·波宁(Thomas Pornin)
14年4月13日在19:31

#3 楼

如果查看RFC6520(心跳扩展),则有效负载后会有填充。因此,需要长度来知道有效负载在哪里结束以及填充开始。除此之外,我发现设计过于工程化:进行此扩展的两个原因似乎都是为了使PMTU成为可能(通过使用不同大小的消息),并通过心跳来了解另一端是否仍然有效或在其中重置一些超时计数器。中间盒的状态。无需在这些情况下反映用户指定的有效负载。

从安全角度来看,这些过度设计的协议只能在其中测试您真正需要的东西,只是乞求将来的麻烦。 。

评论


根据RFC6520,服务器无法将有效载荷与填充区分开,因此如果要求服务器仅发回有效载荷,则需要信任输入,这是一个坏主意。通常,像PKCS1.5或PKCS7这样的填充符使得接收者可以明确地从有效载荷中得知填充符。顺便说一句,他们填充到什么长度?

–wallenborn
14年4月14日在8:26

RFC6520第4节介绍了该结构。有效负载长度字段仅指定有效负载的长度,因此填充只是剩下的数据,直到数据包结束。分组的总长度由TLS帧定义(例如在上一层)。

– Steffen Ullrich
2014年4月14日9:55



啊,我明白了,谢谢。从协议设计的角度来看,最好采用这样的结构:(类型,长度,固定长度的现时,有效负载,填充),其中填充是可识别的模式。这样就没有人需要实际使用长度字段,并且PMTU的发现仍然是可能的。但是我同意,整个过程都是过度设计的。

–wallenborn
14年4月14日在11:51