35

我想加密由 web 应用程序编写的 cookie,并且我想将 cookie 的大小保持在最小,因此我选择了 AES/CTR/NoPadding。

你会推荐使用什么作为 IV 足够随机并且仍然保持应用程序无状态。我知道我可以生成一个随机 IV 并将其附加到消息中,但这会增加 cookie 的大小。

此外,对于 128 位 AES,推荐的 IV 大小是多少?

大家是怎么做的呢?是否存在任何“久经考验”的方法?我不想重新发明轮子。

4

6 回答 6

53

CTR 安全性要求您永远不要将 IV 用于使用相同密钥的两个消息加密。实际上它更严格:CTR 模式通过加密计数器的连续值来工作(IV 只是该计数器的初始值),并且只有在不使用相同的计数器值两次时才能实现适当的安全性;这意味着用 IV 加密一个值实际上“消耗”了一系列连续的 IV 值,这些值不能被另一个加密重用。

最简单的方法是使用加密安全的随机数生成器,并为每条消息创建一个新的 16 字节随机 IV。我强调“加密安全”,因为这很重要;一个基本的随机数生成器是不够的。对于 Java,使用java.util.SecureRandom. 使用 Win32,调用CryptGenRandom(). 通过随机选择,可能的 128 位 IV 的空间足够大,以至于冲突极不可能发生。实际上,这就是 AES 使用 128 位块的原因(因此暗示 128 位 IV)。

将解密消息的实体必须知道 IV,因此您必须将其与加密消息一起存储。那是额外的 16 个字节。我知道这种开销是您想要避免的,尽管 16 个字节对于 cookie 来说并不算多。cookie 的有效最大长度取决于 Web 浏览器,但 4000 个字符似乎“无处不在”。一个 16 字节的 IV,当以字符编码(例如使用 Base64)时,将使用大约 22 个字符,即远小于最大 cookie 大小的 1%:也许你能负担得起?

现在我们可以变得时髦并尝试通过诡计来减少 IV 长度:

  • 使用散列函数生成 IV:在服务器端,使用一个计数器,它从 0 开始,每次需要新的 IV 时都会递增。要获得 IV,您需要使用合适的散列函数(例如 SHA-256)对计数器进行散列,并保留散列值的前 16 个字节。散列函数的“随机化属性”足以使 IV 在 CTR 要求方面具有足够的随机性。这需要一个加密安全的散列函数,因此需要 SHA-256(避免 MD5)。然后,您只需将计数器值存储在 cookie 中,计数器将小于 16 个字节(例如,如果您的客户不超过 40 亿,则计数器将适合 4 个字节)。但是,有一个隐藏的成本:服务器(我假设服务器正在您的系统中执行加密)必须确保它永远不会重用计数器值,因此它必须存储“

  • 使用外部唯一值: cookie 可能是上下文的一部分,它提供足够的数据来生成一个值,该值对于每个加密都是唯一的。例如,如果请求还包含(明确的)“用户 ID”,您可以使用用户 ID 作为 IV 源。设置类似于上面的设置:您获取所有数据,将其填充到 SHA-256 中,SHA-256 输出的前 16 个字节就是您需要的 IV。仅当该数据对于给定的加密消息没有更改并且它确实是唯一的时,这才有效。这是一种罕见的情况:例如,“用户 ID”只有在永远不需要为同一用户重新加密新消息并且永远不可能重用用户 ID(例如老用户退出,新用户出现并选择现在免费的用户 ID)。

使用通过加密安全 PRNG 生成的随机 16 字节 IV 仍然是“安全”的方式,也是我推荐的方式。如果您发现 cookie 中的空间紧张,那么这意味着您正在接近 4 kB 的限制,此时您可能需要使用压缩(在加密之前的数据上;加密之后,压缩非常不可能工作)。使用zlib(在 Java 中,您可以通过 访问 zlib java.util.zip)。

警告:在上述所有内容中,我并不是说cookie 加密是否有助于提供您想要实现的任何安全特性通常,当需要加密时,实际上既需要加密又需要完整性,然后应该使用加密和完整性组合的模式。查找GCMCCM。此外,cookie 加密主要有一个目的,即避免在服务器端存储一些用户特定数据的成本。如果您想为其他用途加密 cookie,例如验证有效用户,那么您做错了:加密不是正确的工具。

于 2011-01-07T14:23:02.383 回答
4

我没有直接回答你的问题,但有几件事要补充。

首先,加密 cookie 对我来说没有意义。如果您希望数据保密,则无论如何都不应该将其存储在 cookie 中。如果您想要完整性(即不可能篡改 cookie 的内容),您应该使用密钥散列(例如 HMAC)。

另一个注意事项是永远不要使用全为 0 的 IV,只是为了方便。

IV的大小与您的块相同。对于 AES-128,块大小为 128,密钥大小为 128,因此 IV 为 128 位。

最好的方法是创建一个随机的 AES 密钥并将其用作 IV。这个随机 IV 可能是公开的,只要它不会在后续使用相同密钥的加密中重复使用

编辑

您可能想查看此 wiki 页面以获取有关使用哪种模式的更多信息。但是,除非您确定应该使用它,否则切勿使用 ECB。即便如此,请与专家核实。据我所知,CBC 是最安全的(与 PCBC 一起)。

http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation

于 2011-01-05T20:12:38.400 回答
2

如果您不使 IV 随机化(即,您使用一些重复的数字组),如果 cookie 始终以相同的明文开头,则更容易找出密钥。

AES-128 的 IV 大小为 128 位。IIRC,IV 与密码块的大小相同。128 位是 16 个字节。如果将其存储为 ASCII 十六进制字符串,则为 32 个字节。这真的太多了吗?这个时代的 32 字节根本不算多……

于 2011-01-05T20:09:31.067 回答
1

可以通过使用 CBC 并将 HMAC 存储在消息前面来避免随机 IV。使用随机选取的常数 IV 是可以的。但是你必须确保消息都是不同的。

当加密的消息总是不同时就是这种情况。带有序列号的许可证密钥将符合此条件。带有用户 ID 或会话 ID 的 cookie 也会匹配。

如果您将 hmac 存储在消息前面,则可以使用带有随机常数 IV 的 CBC。哈希将累积第一个块中消息中传播的所有变化。如果您可以确保它是唯一的或在很长一段时间内不会重复使用,您还可以添加一些随机字节或最好添加一个序列号。

甚至不要考虑使用具有恒定 IV 的 CTR。

于 2017-08-27T08:37:54.630 回答
0

在 cookie 中包含一个大的随机数。64 或 128 位的数字可能足够大。它需要足够大,以便很难获得重复。一定要在这个数字中加入足够的熵。不要只使用 gettime()。如果您有权访问 CRNG,请在此处使用它。

将 256 位主密钥与您的应用程序一起存储。使用 SHA256 导出您的密钥信息。同样,为此使用 CRNG。

$keyblob = sha256( concat("aeskeyid", $masterkey , $randomnumberwithcookie ) )
$aeskey = $keyblob[0..15]
$aesiv = $keyblob[16..31]

您可能还想为 HMAC 派生密钥。

$mackeyblob = sha256( concat("hmackeyid", $masterkey , $randomnumberwithcookie ) )

或者,您可以使用 SHA512 将上述两个哈希运算合并为一个。

$keyblob = sha512( concat("randomkeyid", $masterkey , $randomnumberwithcookie ) )
$aeskey = $keyblob[0..15]
$aesiv = $keyblob[16..31]
$hmackey = $keyblob[32..63] 
于 2011-01-06T01:23:04.273 回答
0

你会推荐使用什么作为 IV 足够随机并且仍然保持应用程序无状态。我知道我可以生成一个随机 IV 并将其附加到消息中,但这会增加 cookie 的大小。

这听起来更棘手,因为您真的不想重复随机数(IV 的随机部分),并且您必须考虑生日绑定,因为这对于所有输入消息都是正确的(在您的情况下为 cookie )。现在您可以说并尝试 2^64 中的一个碰撞机会,但无论如何您都需要一个 127 位计数器;这会让你在计数器溢出之前只剩下一个位;即您的 cookie 大小将最大化为 32 个字节。

就我个人而言,我不喜欢使用完整的 128 位,因为这实际上会增加冲突的机会。我会尝试确定 cookie 的最大大小,将其除以 16 - 向上舍入 - 以确定块的数量,然后将所需的位数保持为 0 以适应该(无符号)数字。然后你可以用随机位填充其他(最左边,最低索引)字节。Cookie 的最大大小为 4096 字节,因此您可以很容易地看到它可以使用一字节计数器。


您可以在此处使用生日攻击的算法来计算特定随机数大小的冲突机会(以位为单位的随机数大小为 log_2(H),因为 H 是 Wikipedia 文章中的空格)。然后,您可以使使用的字节数尽可能小,并计算重复计数器值的风险。

假设您可以承受 1/2^32 的碰撞风险,并且您预计不会超过 40 亿 (~2^24) 个 cookie。我在 WolframAlpha中使用的计算是log_2(n^2 / (2p)) where p = 1 / 2^32, n = 2^24. 然后可以使用大小为 79 位的 nonce 值;让我们将其四舍五入为 80 位或 10 个字节。换句话说,在 Java 中,您将创建一个 16 字节的 IV 并用 10 字节的安全随机数据填充最低索引字节(即计数器的最高有效位,因为 CTR 模式通常是大端)。

由于 cookie 只增加了最高索引字节,因此您将有 5 个备用字节,如果最大 cookie 大小增加,这很好。

于 2021-12-12T12:18:24.120 回答