默认情况下,Ruby on Rails 将会话数据存储在 cookie 中。这有很多优点,例如不需要在服务器端设置任何持久层。但是,会话数据未加密,并且我正在编写的 Rails 应用程序将潜在的敏感数据放入会话中。如果可能,我想避免在服务器端存储会话数据,并且 Rails 唯一现有的加密 cookie 存储实现似乎已被放弃,所以我正在编写自己的加密 cookie 存储。
Rails cookie 会话存储的工作方式如下:
- 它将会话数据序列化为字节字符串。
- 序列化数据转换为base64。
- base64 数据由 HMAC 附加。Rails 要求 HMAC 密钥至少为 30 字节;默认情况下,Rails 生成一个 128 字节的随机字符串作为密钥,从 /dev/urandom 获得。用于 HMAC 的默认散列算法是 SHA-1。
- base64 数据 + HMAC 作为 cookie 发送到 HTTP 客户端。
- 当客户端执行请求时,Rails 首先检查 HMAC 验证是否成功。如果不是,那么它将默默地丢弃 cookie 中的会话数据,就好像用户根本没有发送任何会话数据一样。这也意味着如果管理员更改了 HMAC 密钥,那么所有旧会话都会自动失效。
- 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 上开箱即用。