为Rails编写一个加密的cookie会话存储;我的方法是否安全?

4
默认情况下,Ruby on Rails将会话数据存储在cookie中。这有许多优点,例如不需要在服务器端设置任何持久层。但是,会话数据未加密,并且我正在编写的Rails应用程序将潜在敏感数据放入会话中。如果可能的话,我想避免将会话数据存储在服务器端,而现有的Rails加密cookie存储实现似乎已被废弃,因此我正在编写自己的加密cookie存储。
Rails cookie会话存储的工作方式如下:
1.它将会话数据序列化为字节字符串。 2.序列化数据被转换为base64。 3.base64数据被附加一个HMAC。Rails要求HMAC密钥至少为30个字节;默认情况下,Rails从/dev/urandom获取128个字节的随机字符串作为密钥。用于HMAC的默认哈希算法是SHA-1。 4.base64数据+HMAC作为cookie发送到HTTP客户端。 5.当客户端执行请求时,Rails首先检查HMAC验证是否成功。如果不成功,则会静默丢弃cookie中的会话数据,就好像用户根本没有发送任何会话数据一样。这也意味着,如果管理员更改了HMAC密钥,则所有旧会话都会自动失效。 6.base64数据被解码并取消序列化为Ruby数据结构。
我正在编写的加密cookie会话存储子类化了正常的cookie存储类。它的工作方式如下:
1.在第3步之后插入一个3.5步骤:它将加密数据。 2.修改第5步:在检查HMAC之前,它将解密数据。 3.加密算法是CFB模式下的AES-256。我知道EBC会暴露重复的模式。 4.该代码要求管理员指定一个长度为32字节的加密密钥。加密密钥没有被哈希。默认情况下,它建议从/dev/urandom获得一个随机生成的32字节加密密钥。 5.它还要求管理员指定一个长度为16字节的初始化向量。IV没有被哈希,而且默认情况下,它建议从/dev/urandom获得一个随机生成的IV。
我在加密之前进行HMAC(而不是加密后进行HMAC)的原因是因为我想能够检测到加密密钥或IV的更改。如果加密后进行HMAC,则如果我更改加密密钥或IV,则HMAC验证将通过,这是不可取的。
我的方法安全吗?如果不是,那么缺少什么?
一些注释:
  • 我想采用CTR模式,正如http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html所推荐的那样。但不幸的是,OpenSSL(包含在Ruby标准库中)不支持CTR模式,而我不想要求用户安装单独的第三方加密库。
  • 我想使用SHA-256作为HMAC算法。daemonology.net建议避免使用SHA-512,因为可能存在“32位系统的侧信道攻击”(不管这是什么意思)。然而,并非所有平台都支持HMAC+SHA256算法。尤其是,在OS X上安装的Ruby不支持HMAC+SHA256。我希望我的代码可以在OS X上开箱即用。
3个回答

1

我不确定我是否完全正确地理解了你的意思,但如果是的话,你似乎没有理解使用IV的重点。你似乎计划拥有一个秘密IV,并将其用于每个cookie。

IVs无需保密。此外,您永远不应该重复使用相同的IV和相同的密钥。

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


据我所知,IV 旨在成为 CFB 数据随机化过程的种子。是的,目前的方法保持 IV 的机密性,因为没有必要透露它。加密密钥和 IV 被用于加密每个 cookie。这不应该是一个问题,对吗? - Hongli
@Hongli 我认为Oggy的意思是,你应该为每个cookie生成一个唯一的IV,并将其(未加密)添加到该cookie的前面。 - Inshallah
@Hongli,你的IV不应该由/dev/urandom生成,因为这可能导致两个cookie使用相同的IV。 - Inshallah
@Hongli:如果您使用固定的IV,那么您实际上已经将CFB转换为ECB。对于CTR来说更糟糕,因为使用固定IV会使其完全无用。 - oggy
@oggy:啊,对了。使用 /dev/urandom 生成新 IV 是可接受的吗?Inshalla说可能会得到相同的 IV 但是 IV 是16个字节,所以我认为这不太可能发生。如果 /dev/urandom 不是一个好选择,那么全局计数器是否是一个好主意? - Hongli
1
@Hongli: 对不起,我把CFB和CBC搞混了。重用IV会使CBC变成一种过度宣扬的EBC,但似乎会在选择性明文攻击下破坏CFB。与/dev/random相比,/dev/urandom对于IV可能更实用。由于IV的大小是有限的,最终你总会遇到碰撞,而除非NSA想要你的头,否则/dev/urandom应该没问题。全局计数器(假设你指的是简单的递增计数器)是个可怕的主意。我希望你打算将其与安全(HTTPS)cookie一起使用? - oggy

1

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


0

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

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


后来添加:

还有另一个原因,我后来才想到,为什么在 OP 描述的情况下,仅仅使用 /dev/(u)random 作为 IV 可能不是一个好主意。在 random(4) 的 manpage 中,它说用户应该节约使用 /dev/urandom,否则他们可能会降低设备上其他用户的随机性质量。由于读取的数据量与客户端请求成正比,所以你必须假设在 OP 的情况下最终将读取大量的随机数据。

密钥生成器可能会从 /dev/random 中读取,这会导致阻塞,但这些设备的其他“更合法”的用户(更关心随机性)的随机性将被降低。嗯,阻塞密钥生成器也不是很好。

因此,既然 OP 不想太依赖第三方库,他可以重用 AES 加密函数,按照我上面概述的方式在计数器模式下使用它们(也在 wikipedia article under the heading Designs based on cryptographic primitives 中提到)。


1
当你说/dev/urandom不够好因为“可能”生成相同的2个IV时,你是什么意思?你能把“可能”量化成实际的几率吗?OpenSSL默认使用/dev/urandom来种子化其随机数生成器。所以在这个问题中,每个人都认为/dev/urandom不安全,这超出了我的理解范围... - mox1
@James:我可以看出/dev/urandom是一种实用的方法,而且它可能也非常安全。但是,加密随机数和非常随机的数字之间存在概念上的差异。两者都有其用途,尽管加密随机数最适合用作IV,但在需要真正随机数的情况下不应使用它(同一个数字从不重复出现并不是非常随机的:)。你说OpenSSL使用/dev/urandom来生成其随机数生成器的种子,而不是将其用作其IV的源;你知道OpenSSL是否实际使用非随机数IV吗? - Inshallah
1
在Linux上,/dev/urandom是非阻塞的,但会重复使用种子中可用的熵,而/dev/random将阻塞直到熵池足够充满以产生更多字节。在OSX上,Yarrow算法不会强制执行这样的限制,但代价是在大数据样本(1mb)上稍微减少随机数。 - yonkeltron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接