抢先一步
VMware 提供培训和认证,助您加速进步。
了解更多开发人员经常错误地使用加密来尝试提供真实性。例如,RESTful 应用可能会错误地使用加密的 cookie 来嵌入当前用户的身份。
错误在于,加密只能用于保密,而签名用于验证消息的真实性。在本文中,我将解释并提供一个示例,说明为什么加密不能保证真实性。
如果您只想看代码,可以直接跳到末尾,那里有一个 Java 示例应用,演示了该漏洞。
假设我们想避免在会话中查找用户,而是想将用户信息嵌入到 cookie 中。由于 cookie 可以被恶意用户修改,我们需要能够验证提供的 cookie 是否由我们的应用服务器创建。
为了防止用户篡改 cookie,我们**错误地**决定使用 AES 加密(采用 CBC 模式)来加密 cookie,而不是对 cookie 进行签名。我们的 cookie 如下所示,已正确加密(但错误地未签名):
Cookie = Base64String( IV, aes_cbc(k, IV, plainText) )
例如
**注意**:将 IV 与加密文本一起包含在明文中是安全且常见的做法。由于 IV 是固定字节数,因此可以轻松地从组合的 IV、加密值字节数组中提取。
在我们继续之前,了解 XOR(异或)非常重要。为了帮助您回忆,这里是 XOR 的真值表:
A | B | 输出 |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
为了理解我们将如何冒充另一个用户,我们首先需要稍微了解一下 AES / CBC 的工作原理。AES 是一种分组密码,这意味着我们的消息被分解成固定大小的块,然后对每个块执行操作。
解密 AES / CBC 时,第一个块的解密值会与 IV 进行 XOR 运算。例如,以下情况成立:
decrypt(k, encrypted_first_block) XOR IV = plaintext_first_block
为了更好地理解,我们来看一个具体的例子。假设以下情况为真:
**注意**:我们的示例为了简化,使用了 8 位块大小而不是实际的 128 位块大小。这使得人类更容易理解。
这意味着我们的 plaintext_first_block 将是 01110111(ASCII 中的 "w")。我们的工作如下所示:
decrypt(k, encrypted_first_block)
XOR IV
------------
plaintext_first_block
11011101
XOR 10101010
------------
01110111 // "w" ASCII
结合以上信息,我们可以修改解密后的值。具体来说,给定:
我们可以计算出一个修改后的 IV,命名为 IV',它将与原始的有效加密值结合,以冒充另一个用户。
第一步是通过将 IV 中的所有位与 first_block_plaintext 进行 XOR 运算来抵消它们,从而计算出未知值 decrypt(k, encrypted_first_block)。我们的工作如下图所示:
IV
XOR plaintext_first_block
------------
decrypt(k, encrypted_first_block)
10101010
XOR 01110111
------------
11011101
最后一步是通过执行 decrypt(k, encrypted_first_block) XOR desired_plaintext_first_block 来计算 IV'。同样,我们的工作如下图所示:
decrypt(k, encrypted_first_block)
XOR desired_plaintext_first_block
------------
IV'
11011101
XOR 01100001 // "a" ASCII
------------
10111100
现在我们可以验证,提供 IV' 和原始加密值将导致解密结果为 "a",而不是 "w"。
decrypt(k, encrypted_first_block)
XOR IV'
------------
desired_plaintext_first_block
11011101
XOR 10111100
------------
01100001 // This is "a" ASCII
这表明如果我们提供 IV'(而不是 IV)以及原始加密值,它将被解密为 "a"。
现在我们已经了解了如何创建一个修改后的 IV 来使加密值变成我们想要的任何内容,接下来我们探讨一下这如何应用于我们作为用户进行身份验证,然后修改加密 cookie 来冒充另一个用户。
在修改解密值一节中,我们提到在执行该漏洞之前需要一些信息。让我们看看如何通过加密 cookie 获取漏洞所需的信息:
现在我们拥有了必要的信息并理解了如何修改加密值,很容易看出我们可以冒充任何想要的用户。只要我们拥有一个有效帐户,就可以创建一个 IV',将加密 cookie 中的用户名更改为我们选择的任何目标用户。
对该漏洞不信服?通过运行 github 上的示例项目来查看演示。要运行该示例,请将其作为 Maven 项目导入您喜欢的 IDE,然后运行 demo.Main 类。
您将看到我们以 "winch" 身份进行身份验证,但能够修改加密的 cookie 以冒充名为 "admin" 的用户。
至此,我希望您已经相信加密不是提供真实性的有效方法。相反,我们需要确保 cookie 已签名。一种解决方案是使用认证加密,它提供了机密性、完整性和真实性。
请记住,仅仅对 cookie 进行签名并不能保证其安全。还有其他攻击向量,例如重放攻击,必须加以解决才能使解决方案安全。
我们必须认识到,安全很难实现,不应该由个人或少数人来实现。相反,安全最好在一个能够相互检查错误的环境中实现。