4

默认情况下,Ruby on Rails 将会话数据存储在 cookie 中。这有很多优点,例如不需要在服务器端设置任何持久层。但是,会话数据未加密,并且我正在编写的 Rails 应用程序将潜在的敏感数据放入会话中。如果可能,我想避免在服务器端存储会话数据,并且 Rails 唯一现有的加密 cookie 存储实现似乎已被放弃,所以我正在编写自己的加密 cookie 存储。

Rails cookie 会话存储的工作方式如下:

  1. 它将会话数据序列化为字节字符串。
  2. 序列化数据转换为base64。
  3. base64 数据由 HMAC 附加。Rails 要求 HMAC 密钥至少为 30 字节;默认情况下,Rails 生成一个 128 字节的随机字符串作为密钥,从 /dev/urandom 获得。用于 HMAC 的默认散列算法是 SHA-1。
  4. base64 数据 + HMAC 作为 cookie 发送到 HTTP 客户端。
  5. 当客户端执行请求时,Rails 首先检查 HMAC 验证是否成功。如果不是,那么它将默默地丢弃 cookie 中的会话数据,就好像用户根本没有发送任何会话数据一样。这也意味着如果管理员更改了 HMAC 密钥,那么所有旧会话都会自动失效。
  6. base64 数据经过 de-base64 处理并解组为 Ruby 数据结构。

我正在编写的加密 cookie 会话存储是普通 cookie 存储类的子类。它的工作原理如下:

  • 它插入了第 3.5 步:在第 3 步之后,它将加密数据。
  • 它修改了第 5 步:在检查 HMAC 之前,它解密数据。
  • 加密算法为 CFB 模式下的 AES-256。我知道 EBC 会暴露重复模式。
  • 该代码要求管理员指定正好为 32 个字节的加密密钥。加密密钥未经过哈希处理。默认情况下,它建议从 /dev/urandom 获得随机生成的恰好 32 字节的加密密钥。
  • 它还要求管理员指定正好为 16 个字节的初始化向量。IV 没有经过哈希处理,默认情况下,它建议使用从 /dev/urandom 获得的随机生成的 IV。

我之所以在加密之前使用 HMAC(而不是在加密之后使用 HMAC)是因为我希望能够检测到加密密钥或 IV 的更改。如果更改了加密密钥或 IV,我希望该软件自动使旧会话无效。如果我在加密后进行 HMAC,那么如果我更改加密密钥或 IV,HMAC 验证将通过,这是不可取的。

我的方法安全吗?如果没有,那还缺少什么?

几点注意事项:

  • 我想按照http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html的建议使用 CTR,但不幸的是 OpenSSL(包含在 Ruby 标准库中)没有t 支持 CTR,我不想要求我的用户安装单独的第三方加密库。
  • 我想为 HMAC 使用 SHA-256。daemonology.net 建议不要使用 SHA-512,因为可能存在“对 32 位系统的侧信道攻击”(无论这意味着什么)。但是,并非每个平台都支持 SHA-256 作为 HMAC 的散列算法。最值得注意的是,OS X 上的 Ruby 不支持 HMAC+SHA256。我希望我的代码也能在 OS X 上开箱即用。
4

3 回答 3

1

我不确定我是否 100% 正确地阅读了您,但如果我是,您似乎错过了使用 IV 的意义。您似乎计划有一个秘密 IV,您将用于每个 cookie。

IV 没有必要保密。此外,您永远不应使用相同的密钥重复使用相同的 IV。

除此之外,我没有看到任何重大缺陷。这并不意味着它们不存在。

于 2009-07-17T23:34:35.207 回答
1

OpenSSL 不需要单独的 CTR 模式。要实现 CTR 模式,您需要保留一个块大小的计数器并在 ECB 模式下对该计数器进行加密。然后,您将加密的计数器与明文块进行异或并增加计数器。解密是相同的过程(因此 cookie 必须包含其初始计数器值)。您绝不能对相同的键重复使用相同的计数器值,因此请确保仅在更改键时才重置计数器。

于 2009-07-23T03:55:28.833 回答
0

正如 oggy 已经指出的那样,您应该生成一个唯一的 IV 并将其添加到每个 cookie 中。要生成唯一的 IV,/dev/urandom 还不够好,因为它可能会为两个单独的 cookie 生成相同的 IV。

虽然我不是专家,但我认为生成唯一 IV 的一种有效方法是加密一个计数器,该计数器为每个 cookie 递增,其密钥与用于加密 cookie 数据的密钥不同。


很久以后添加:

还有另一个原因,我后来才想到,为什么在 OP 概述的场景中将 /dev/(u)random 专门用于 IV 可能不是一个好主意。在手册页 random(4) 中,它说用户在使用 /dev/urandom 时应该是经济的,否则它们可能会降低设备其他用户的随机质量。由于读取的数据量与客户端请求成正比,因此您必须假设在 OP 的场景中最终会读取大量随机性。

密钥生成器可能会从 /dev/random 读取,这会阻塞,但这些设备的其他更合法的用户(对随机性更感兴趣)的随机性会降低。好吧,阻止密钥生成器也不是很好。

因此,由于 OP 不想过多地依赖 3rd 方库,因此他可以重用 AES 加密函数,并在我上面概述的计数器模式下使用它们(也在基于加密的标题设计下的维基百科文章中原语

于 2009-07-18T03:47:48.093 回答