通过在会话变量和表单中存储随机数来实现 CSRF 保护

发布于 2024-08-21 10:34:39 字数 181 浏览 9 评论 0原文

为了防止 CSRF,您应该将随机数放入表单的隐藏字段、cookie 或会话变量中。但是,如果用户在不同的选项卡中打开多个页面怎么办?在这种情况下,每个选项卡都会有一个带有唯一随机数的表单,但会话变量或 cookie 中只会存储一个随机数。或者,如果您尝试将所有随机数存储在 cookie/session 变量中,您将如何识别哪个随机数属于哪种形式?

To protect against CSRF you should put a nonce in a hidden field in the form, and in a cookie or in the session variable. But what if the user opens several pages in different tabs? In this case each tab would have a form with a unique nonce, but there would be only one nonce stored in the session variable or cookie. Or if you try to store all the nonces in the cookie/session variable, how would you identify which one belongs to which form?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

大姐,你呐 2024-08-28 10:34:39

您可以在每个表单中存储相同的随机数。最简单的方法是将随机数与会话 ID 联系起来,以便这些表单仅在该会话中起作用。

您需要让攻击者难以窃取会话 ID 并创建自己的随机数。因此,解决此问题的一种方法是使用 HMAC-SHA256(或类似的)来散列会话 ID,并使用不向公众公开的密钥。

(显然,如果攻击者本身能够获取实际的会话 ID,他们就已经可以进行会话劫持。所以这不是我要讨论的内容,而是攻击者制作脚本(在受害者的计算机上运行)的能力可以以某种方式获取会话 ID 并使用它动态生成预先填充随机数的 URL。)


ETA:上述方法本身是否足够取决于您期望典型会话持续多长时间。如果用户通常使用持续时间超过几个小时的持久会话,您将需要使用更复杂的东西。

一种方法是为每个表单创建一个新的随机数,其中包含时间戳以及 hash(timestamp . sessionid) (其中 hash 是 HMAC 的某种变体,如下所述上面,为了防止伪造,.是字符串连接)。然后,您可以通过以下方式验证随机数:

  1. 检查时间戳以确保随机数足够新鲜(这取决于您的策略,但通常需要几个小时)
  2. 然后,根据时间戳和会话 ID 计算哈希值,并与nonce,验证 nonce 是否真实

如果 nonce 检查失败,您将需要显示一个新表单,其中预先填充了用户提交的内容(这样,如果他们花了一整天的时间来写帖子,他们就不会失去所有的辛勤工作),以及一个新的随机数。然后用户可以直接重新提交成功。

You can store the same nonce in each of the forms. The easiest way to do it is to tie the nonce to the session ID, so that those forms only work in that session.

You will want to make it hard for attackers to snarf session IDs and create their own nonces. So, one way to go about it is to use HMAC-SHA256 (or the like) to hash the session ID, using a key that you do not expose to the public.

(Obviously if the attacker can get the actual session ID itself, they can already do session hijacking. So that's not what I'm talking about, but rather the ability for an attacker to craft a script (that runs on the victim's computer) that can somehow grab the session ID and use that to dynamically generate a URL with the nonce pre-filled.)


ETA: Whether the above approach is enough on its own depends on how long you expect your typical sessions to last. If users usually use long-lasting sessions spanning longer than a few hours, you'll need to use something more sophisticated.

One approach is to create a new nonce for each form, that contains the timestamp, as well as hash(timestamp . sessionid) (where hash is some variant of HMAC as described above, to prevent forgery, and . is string concatenation). You then verify the nonce by:

  1. checking the timestamp to ensure that the nonce is fresh enough (this is up to your policy, but a few hours is typical)
  2. then, calculating the hash based on the timestamp and session ID, and comparing against the nonce, to verify that the nonce is authentic

If the nonce check fails, you'll want to display a new form, pre-populated with the user's submission (so that if they took a whole day to write their post, they won't lose all their hard work), as well as a fresh nonce. Then the user can resubmit straight away successfully.

西瑶 2024-08-28 10:34:39

有些人确实为每个表单生成一个令牌,这是一种非常安全的方法。但是,这可能会破坏您的应用程序并激怒用户。为了防止所有针对您站点的 XSRF,您只需要每个会话有唯一的 1 个令牌变量,然后攻击者将无法伪造任何请求,除非他能找到这 1 个令牌。这种方法的一个小问题是,只要受害者正在访问攻击者控制的网站,攻击者就可以暴力破解该令牌。然而,如果令牌非常大,例如 32 字节左右,那么暴力破解将需要很多年的时间,并且 http 会话应该在此之前很久就过期了。

Some people do generate a token for each form, and that is a very secure approach. However, this can break your app and piss off users. To prevent all XSRF against your site you just need unique 1 token variable per session and then the attacker will not be able to forge any request unless he can find this 1 token. The minor issue with this approach is that the attacker could brute force this token as long as the victim is visiting a website the attacker controls. HOWEVER if the token is pretty large like 32 bytes or so, then it would take many years to brute force, and the http session should expire long before then.

温馨耳语 2024-08-28 10:34:39

您所描述的不再是随机数(随机数 = 使用一次的数字),它只是一个会话标识符。随机数的全部意义在于,它仅对单个表单提交有效,因此比会话 ID 提供了更高的防劫持安全性,但代价是无法在站点上并行操作多个选项卡。

对于许多目的来说,随机数都有些过分了。如果您使用它们,则应该仅在对系统进行关键更改的表单上设置和要求它们,并教育用户他们不能期望并行使用多个此类表单。未设置随机数的页面应注意不要从会话中清除任何先前存储的随机数,以便用户仍然可以与随机数表单并行使用非随机数页面。

What you're describing is not a nonce anymore (nonce = number used once), it's just a session identifier. The whole point of a nonce is that it is only valid for a single form submission, therefore offers greater security against hijacking than just a session ID, but at the cost of not being able to have multiple tabs operating in parallel on the site.

Nonces are overkill for many purposes. If you use them, you should only set and require them on forms that make critical changes to the system, and educate users that they cannot expect to use more than one such form in parallel. Pages which do not set a nonce should take care not to clear any previously stored nonce from the session, so that users can still use non-nonced pages in parallel with a nonced form.

若有似无的小暗淡 2024-08-28 10:34:39

很久以前就写了这篇文章。
我已经实现了一个 csrf 拦截器,我几乎可以肯定它可以很好地保护它。
它确实可以在多个打开的窗口下运行,但我仍在评估它提供的保护类型。它使用数据库方法,即存储而不是会话到表。
注意:在这种情况下,我使用 MD5 作为一种简单的反 SQLI 机制

伪代码:

FORM:

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

Long time back this post was written.
I've implemented a csrf blocker that I'm almost certain protects well.
It does function with multiple open windows, but I'm still assessing the kind of protection it offers. It uses a DB approach, ie storing instead of session to a table.
NOTE: I use MD5 in this case as an easy anti-sqli mechanism

Pseudo Code:

FORM:

token = randomstring #to be used in form hidden input
db->insert into csrf (token, user_id) values (md5(token),md5(cookie(user_id))

-- the token is then kept in the db till it's accessed from the action script, below:

ACTION SCRIPT:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文