为Rails编写加密的cookie会话存储;我的方法安全吗?

时间:2009-07-17 22:59:30

标签: ruby-on-rails security encryption cryptography aes

默认情况下,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'ed并解组为Ruby数据结构。
  7. 我正在编写的加密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的建议使用点击率,但遗憾的是OpenSSL(包含在Ruby标准库中)不支持点击率,我不想要求我的用户单独安装第三方加密库。
    • 我想将SHA-256用于HMAC。 daemonology.net建议不要使用SHA-512,因为潜在的“对32位系统的侧通道攻击”(无论这意味着什么)。但是,并非每个平台都支持SHA-256作为HMAC的哈希算法。最值得注意的是,OS X上提供的Ruby不支持HMAC + SHA256。我希望我的代码能够在OS X上开箱即用。

3 个答案:

答案 0 :(得分:1)

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

IV不需要秘密。此外,您永远不应该使用相同的密钥重复使用相同的IV。

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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

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


稍后添加:

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

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

因此,由于OP不希望如此依赖第三方库,他可以重用AES加密函数,在计数器模式下使用它们,如上所述(也在wikipedia article under the heading Designs based on cryptographic primitives中)。