加密的 Cookie 是否容易受到 Padding Oracle 攻击

信息安全 攻击 饼干 甲骨文
2021-09-10 22:00:20

我目前正在帮助用水晶语言编写一个快速编译的 Web 框架,并试图找到一种使用加密 cookie 加速会话的方法。我们目前正在使用 json 字符串并使用 AES 对其进行加密。然后我们对它进行 base64 编码和签名。

我的问题是:是否需要签名或修改后的加密字符串无法解密?如果它没有生成可以解析为会话的有效 json,它将为空,就像使用无效签名一样。我的理解是填充 oracle 攻击通过从服务器获取反馈来工作。一个加密的空 json 字符串就足够了吗?

3个回答

简短的回答是,您不能依靠加密来确保消息的完整性。例如,请参见此处。

为了说明为什么该一般性陈述对于这个问题的特定示例也是正确的,让我们看看攻击者如何修改加密消息。为简单起见,我假设您使用的是 AES-CBC 加密。让我们考虑以下字符串:

{"Name":"Ryan Archer","Crime":"First Degree Murder","Judge 1":"Afred E Newman Jr","Verdict":"Not Guilty"}

当一个 AES-CBC 加密上述字符串时,它将在以下 16 字节块中加密:

Block 0:   "Name":"Ryan Arc
Block 1:   her","Crime":"Fi
Block 2:   rst Degree Murde
Block 3:   r","Judge 1":"Al
Block 4:   fred E Newman Jr
Block 5:   ","Verdict":"Not
Block 6:   Guilty" 

并且加密字符串的每个块将与原始明文字符串的同一块对应。

现在这是 CBC 的一个有趣特征。即使您不知道加密密钥,您也可以修改加密文本,以便在解密时以特定方式修改明文。

让我们以上面的例子为例。如果有人要用某个密钥对这个字符串进行 AES-CBC 加密,而我不知道这个密钥,如果我将加密文本块 4 的最后 3 个字节与字符串“Not”进行异或,那么对解密文本的影响将如下:

  • 块 4 将以未知的随机方式损坏。
  • 块 5 的最后 3 个字节将与字符串“Not”进行异或

因此,解密后的字符串将如下所示:

Block 0:   "Name":"Ryan Arc
Block 1:   her","Crime":"Fi
Block 2:   rst Degree Murde
Block 3:   r","Judge 1":"Al
Block 4:   %$#$%@#%$@#%#%#$ (random garbage bytes)
Block 5:   ","Verdict":"
Block 6:   Guilty" 

并且将被读取为以下有效的 json 字符串:

"Name":"Ryan Archer","Crime":"First Degree Murder","Judge 1":"Al%$#$%@#%$@#%#%#$","Verdict":" Guilty"

法官的名字被随机破坏,但攻击者成功地修改了消息的关键部分 - Ryan Archer 现在犯有谋杀罪!

当然,并非所有字符串都以这种方式构造,块 4 中的随机字节可能会破坏字符串,使其不再是有效的 json 字符串。但是攻击者可以通过对加密字符串的各种修改多次尝试这种攻击,迟早会得到一个有效的 json 字符串。

是否需要签名或修改后的加密字符串无法解密?

在密码学世界中,修改后的消息称为篡改消息。加密本身并不能提供防止篡改的保护,但分组密码并不是单独使用的。分组密码用于不同的模式,fe ECB、CBC、CTR、GCM 等。不同的模式具有不同的属性,其中一些提供防篡改保护(又名 Authenticated Encryption,fe GCM),而另一些则不提供。

如果您使用的是 Authenticated Encryption 模式,则被篡改的消息将无法解密。但是,如果您使用其他模式,例如 CBC 或 CTR(这些是我的经验中最普遍的),除非出现填充错误,否则被篡改的消息会很高兴地解密

如果它没有生成可以解析为会话的有效 json,它将为空,就像使用无效签名一样。

这将取决于您的应用程序如何处理无效的 json,这里可能会出现填充预言。

我的理解是填充 oracle 攻击通过从服务器获取反馈来工作。一个加密的空 json 字符串就足够了吗?

当攻击者发送一个被篡改的消息并且能够区分填充是否正确时,就会发生填充预言攻击。这就是为什么我之前说过,您的应用程序如何处理无效 JSON 可能会衍生为填充预言机

想象一下,攻击者发送了一条带有错误填充的篡改消息,您的应用程序将尝试解密该消息,但会抛出错误的填充异常。之后,攻击者再次篡改消息,但这次他能够伪造正确的填充,您的应用程序将正确解密消息,但内容将是无效的 JSON,应用程序将尝试解析无效的 JSON 并抛出错误无法解析

如果攻击者能够区分这两种情况,导致返回的 HTTP 状态不同,或者堆栈跟踪显示不同的错误消息,或者只是通过计时响应时间。然后攻击者有一个填充oracle,可以从cookie中恢复明文

为防止出现这种情况,您应始终在无法解密消息和解密但内容无效时提供相同的错误消息。此外,您应该确保两种情况都需要相同的时间来响应。

IMO 最好的方法是使用经过身份验证的加密模式,让您一起加密和签名,并让加密框架/库处理它

顺便说一句,我提到了一种永远不应该使用的加密模式。它是 ECB,如果您使用此模式(某些库默认使用此模式),请更改它,因为它是弱模式

是否需要签名或修改后的加密字符串无法解密?

验证消息的经典(和更快)方法是使用MAC如果您需要不可否认性(您可能不需要),您应该使用签名。

如果它没有生成可以解析为会话的有效 json,它将为空,就像使用无效签名一样。[...] padding oracle 攻击通过从服务器获取反馈来工作。一个加密的空 json 字符串就足够了吗?

关于填充预言机攻击:

AES 是一种块加密。AES 中没有填充。如果您的实现使用易受攻击的填充(如 PKCS7 所做的那样),理论上可能会进行填充 oracle 攻击。但是,AES 是 CBC 加密(从随机混乱开始),这意味着相同的明文每次都会产生不同的密文。攻击者很难猜到您返回了一个空的 json 字符串。

但是,可以通过其他方式检测填充错误。如果您在 REST 实现中使用不同的 HTTP 错误代码,或者通过测量服务器响应时间。MAC 将在这里提供帮助,因为攻击者无法篡改您的消息。如果 MAC 无效并且解密永远不会发生,您将立即丢弃该消息。

您可能希望在响应中包含随机时间,以便更难分析您的答案和/或为所有内容返回相同的 http 代码以进一步提高安全性,但我认为后者会导致比收益更多的问题。当然,您不应该透露您已检测到填充错误。