【发布时间】:2011-01-16 01:19:09
【问题描述】:
为了防止 CSRF,您应该在表单的隐藏字段、cookie 或会话变量中放置一个 nonce。但是,如果用户在不同的选项卡中打开多个页面怎么办?在这种情况下,每个选项卡都会有一个带有唯一 nonce 的表单,但会话变量或 cookie 中只会存储一个 nonce。或者,如果您尝试将所有 nonce 存储在 cookie/session 变量中,您将如何识别哪个属于哪个表单?
【问题讨论】:
为了防止 CSRF,您应该在表单的隐藏字段、cookie 或会话变量中放置一个 nonce。但是,如果用户在不同的选项卡中打开多个页面怎么办?在这种情况下,每个选项卡都会有一个带有唯一 nonce 的表单,但会话变量或 cookie 中只会存储一个 nonce。或者,如果您尝试将所有 nonce 存储在 cookie/session 变量中,您将如何识别哪个属于哪个表单?
【问题讨论】:
您所描述的不再是随机数(随机数 = 使用次数一次),它只是一个会话标识符。 nonce 的全部意义在于它仅对单个表单提交有效,因此提供了比仅会话 ID 更高的安全性来防止劫持,但代价是无法在站点上并行运行多个选项卡。
Nonce 在很多情况下都是多余的。如果您使用它们,您应该只在对系统进行关键更改的表单上设置和要求它们,并告知用户他们不能期望并行使用多个这样的表单。未设置 nonce 的页面应注意不要从会话中清除任何先前存储的 nonce,以便用户仍然可以与 nonced 表单并行使用 non-nonced 页面。
【讨论】:
很久以前写过这篇文章。 我已经实现了一个我几乎可以肯定可以很好地保护的 csrf 阻止程序。 它确实适用于多个打开的窗口,但我仍在评估它提供的保护类型。它使用数据库方法,即存储而不是会话到表。 注意:在这种情况下,我使用 MD5 作为一种简单的反sqli 机制
伪代码:
表格:
token = randomstring #to be used in form hidden input
db->insert into csrf (token, user_id) values (md5(token),md5(cookie(user_id))
-- 然后将令牌保存在数据库中,直到从下面的操作脚本中访问它:
动作脚本:
if md5(post(token)) belongs to md5(cookie(user_id))
#discard the token
db -> delete from csrf where token=md5(post(token)) and user_id=md5(cookie(user_id))
do the rest of the stuff
【讨论】:
有些人会为每个表单生成一个令牌,这是一种非常安全的方法。但是,这可能会破坏您的应用程序并激怒用户。为了防止所有 XSRF 针对您的站点,您只需要每个会话唯一的 1 个令牌变量,然后攻击者将无法伪造任何请求,除非他能找到这 1 个令牌。这种方法的一个小问题是,只要受害者正在访问攻击者控制的网站,攻击者就可以暴力破解这个令牌。但是,如果令牌非常大,例如 32 字节左右,那么暴力破解需要很多年,并且 http 会话应该在此之前很久就过期。
【讨论】:
您可以在每个表单中存储相同的随机数。最简单的方法是将 nonce 绑定到会话 ID,以便这些表单仅在该会话中有效。
您需要让攻击者难以窃取会话 ID 并创建自己的随机数。因此,一种解决方法是使用 HMAC-SHA256(或类似方法)对会话 ID 进行哈希处理,使用您不向公众公开的密钥。
(很明显,如果攻击者自己可以获取实际的会话 ID,他们就已经可以进行会话劫持了。所以这不是我要说的,而是攻击者制作脚本的能力(在受害者的计算机),它可以以某种方式获取会话 ID 并使用它来动态生成预先填充随机数的 URL。)
ETA:上述方法本身是否足够取决于您期望典型会话持续多长时间。如果用户通常使用持续时间超过几个小时的长时间会话,则您需要使用更复杂的方法。
一种方法是为每个表单创建一个新的 nonce,其中包含时间戳以及 hash(timestamp . sessionid)(其中 hash 是如上所述的 HMAC 的一些变体,以防止伪造,. 是字符串级联)。然后,您可以通过以下方式验证 nonce:
如果随机数检查失败,您需要显示一个新表单,其中预先填充了用户提交的内容(这样,如果他们花了一整天的时间来写帖子,他们就不会失去所有的辛勤工作) ,以及一个新鲜的随机数。然后用户可以直接重新提交成功。
【讨论】: