【问题标题】:Writing an encrypted cookie session store for Rails; is my approach secure?为 Rails 编写加密的 cookie 会话存储;我的方法安全吗?
【发布时间】:2010-11-11 21:01:49
【问题描述】:

默认情况下,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 数据经过去 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 标准库中)不支持 CTR,我不想要求我的用户安装单独的第三方加密库。
  • 我想为 HMAC 使用 SHA-256。 daemonology.net 建议不要使用 SHA-512,因为可能存在“对 32 位系统的侧信道攻击”(无论这意味着什么)。但是,并非每个平台都支持 SHA-256 作为 HMAC 的散列算法。最值得注意的是,OS X 上的 Ruby 不支持 HMAC+SHA256。我希望我的代码也能在 OS X 上开箱即用。

【问题讨论】:

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


    【解决方案1】:

    正如 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 加密函数,在计数器模式下使用它们,正如我上面所概述的(也在 wikipedia article under the heading Designs based on cryptographic primitives 中。

    【讨论】:

    • 你说 /dev/urandom 不够好是什么意思,因为“它有可能”生成相同的 2 个 IV。您能否将其可能性量化为实际赔率? OpenSSL 默认使用 /dev/urandom 为其随机数生成器播种。所以这个问题中的每个人都认为 /dev/urandom 不安全是我无法理解的......
    • @James:我可以看到 /dev/urandom 是一种实用的方法,而且它可能也非常安全。但是,加密随机数和 非常 随机数之间存在概念上的差异。两者都有其用途,虽然密码随机数最适合作为 IV,但您不会希望在需要 true 随机数的情况下使用它(相同的数字从不出现两次并不是很随机:)。您说 OpenSSL 使用 /dev/urandom 为其随机数生成器播种,而不是将其用作其 IV 的源;你知道 OpenSSL 是否真的使用 non-nonce IVs 吗?
    • @James 在 Linux 上,/dev/urandom 是非阻塞的,但会重用种子中的可用熵,而 /dev/random 将阻塞,直到熵池足够满以产生更多字节。在 OSX 上,Yarrow 算法不会以在大数据样本 (1mb) 上稍微减少随机数为代价施加这样的限制。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

      • 据我所知,IV 旨在成为 CFB 数据随机化过程的种子。是的,目前的方法将 IV 保密,因为没有必要透露它。加密密钥和 IV 用于加密每个 cookie。这应该不是问题吧?
      • @Hongli 我认为 oggy 的意思是您应该为每个 cookie 生成一个唯一的 IV 并将其(未加密)添加到该 cookie。
      • @Hongli 你的 IV 不应该由 /dev/urandom 生成,因为这可能导致同一个 IV 被用于两个 cookie。
      • @Hongli:如果您使用固定的 IV,您实际上已经将您的 CFB 变成了 ECB。如果使用固定的 IV,CTR 会变得更糟。
      • @Hongli:对不起,我把 CFB 和 CBC 弄混了。重用 IV 会将 CBC 变成美化的 EBC,但似乎会在自适应选择明文攻击下破坏 CFB。 /dev/urandom 对于 IV 来说可能比 /dev/random 更实用。由于 IV 的大小是有限的,最终无论如何你都会遇到冲突,并且 /dev/urandom 应该没问题,除非 NSA 想要你的头。全局计数器(假设您的意思是一个简单的增量计数器)是一个糟糕的主意。我希望您将它与安全 (HTTPS) cookie 结合使用?
      猜你喜欢
      • 2018-10-10
      • 2011-07-25
      • 2014-06-14
      • 2015-06-12
      • 1970-01-01
      • 2011-08-09
      • 2010-09-16
      • 1970-01-01
      • 2014-04-11
      相关资源
      最近更新 更多