我目前正在帮助用水晶语言编写一个快速编译的 Web 框架,并试图找到一种使用加密 cookie 加速会话的方法。我们目前正在使用 json 字符串并使用 AES 对其进行加密。然后我们对它进行 base64 编码和签名。
我的问题是:是否需要签名或修改后的加密字符串无法解密?如果它没有生成可以解析为会话的有效 json,它将为空,就像使用无效签名一样。我的理解是填充 oracle 攻击通过从服务器获取反馈来工作。一个加密的空 json 字符串就足够了吗?
我目前正在帮助用水晶语言编写一个快速编译的 Web 框架,并试图找到一种使用加密 cookie 加速会话的方法。我们目前正在使用 json 字符串并使用 AES 对其进行加密。然后我们对它进行 base64 编码和签名。
我的问题是:是否需要签名或修改后的加密字符串无法解密?如果它没有生成可以解析为会话的有效 json,它将为空,就像使用无效签名一样。我的理解是填充 oracle 攻击通过从服务器获取反馈来工作。一个加密的空 json 字符串就足够了吗?
简短的回答是,您不能依靠加密来确保消息的完整性。例如,请参见此处。
为了说明为什么该一般性陈述对于这个问题的特定示例也是正确的,让我们看看攻击者如何修改加密消息。为简单起见,我假设您使用的是 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”进行异或,那么对解密文本的影响将如下:
因此,解密后的字符串将如下所示:
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 代码以进一步提高安全性,但我认为后者会导致比收益更多的问题。当然,您不应该透露您已检测到填充错误。