为什么我们需要 csrftoken cookie 和 HTML 表单的隐藏输入的 csrfmiddlewaretoken 值?

发布于 2024-10-30 23:03:53 字数 834 浏览 1 评论 0原文

我正在努力学习安全知识。我很好奇为什么在 Django 中提交表单(POST)时,有 2 个单独的元素包含相同的 csrf 令牌值:

  • The csrftoken cookie: {' csrftoken':'1effe96056e91a8f58461ad56c0d4ddc',...

  • 表单的隐藏 csrfmiddlewaretoken: < QueryDict: {u'csrfmiddlewaretoken': [u'1effe96056e91a8f58461ad56c0d4ddc'], ...

如果 Django 将隐藏的 csrf 字段/值插入到 将其发送到浏览器 (GET) 时的表单,并期望 收到POST时返回相同的值,那为什么呢 还需要设置cookie吗?

一个更普遍的问题,如果其中任何一个缺失(表单、cookie),您能否提供一个场景来解释如何利用它(安全攻击)?

顺便说一下,我进行了一些简单的测试以确保 Django 分别检查每一个的有效性 确实是这样:

  • 如果我在执行 POST 之前更改表单的 csrf 值, 我收到此调试错误:

    <块引用>

    CSRF 令牌缺失或不正确

  • 如果我在执行 POST 之前删除 csrf cookie, 我收到了不同的错误:

    <块引用>

    未设置 CSRF cookie。

我只是熟悉基本的 csrf 概念并且想要 了解 django 如何帮助防范此类攻击。

I'm trying to learn about security. I am curious about why, in Django, when submitting a form (a POST), there are 2 separate elements that contain the same csrf token value:

  • The csrftoken cookie: {'csrftoken': '1effe96056e91a8f58461ad56c0d4ddc', ...

  • The form's hidden csrfmiddlewaretoken: <QueryDict: {u'csrfmiddlewaretoken': [u'1effe96056e91a8f58461ad56c0d4ddc'], ...

If Django is inserting the hidden csrf field/value to
the form when it sends it to the browser (GET), and expects the
same value back when receiving the POST, then why is it
necessary to also set a cookie?

A more general question, if either of them was missing (form, cookie), could you provide a scenario that explains how this could be exploited (security attack)?

By the way, I ran a couple of simple tests to make sure that
Django was checking the validity of each one separately and
indeed it is:

  • If I change the form's csrf value before doing the POST,
    I get this debug error back:

    CSRF token missing or incorrect

  • If I delete the csrf cookie before doing the POST,
    I get a different error back:

    CSRF cookie not set.

I'm just familiar with basic csrf concepts and want to
learn how django helps protect against these types of attacks.

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

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

发布评论

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

评论(4

平安喜乐 2024-11-06 23:03:53

来自 Jeff Atwood 的博客文章:

防止 CSRF 和 XSRF 攻击
(2008 年 10 月 14 日)

原始帖子

泽勒论文(pdf)推荐“双重提交”
cookie”方法来防止XSRF:

当用户访问某个网站时,该网站应生成
(加密强度高)伪随机值并将其设置为
用户机器上的 cookie。该网站应该要求每一种形式
提交以包含此伪随机值作为表单值,并且
也作为 cookie 值。当 POST 请求发送到站点时,
仅当表单值和
cookie 值是相同的。当攻击者提交表单时
代表一个用户,他只能修改表单的值。一个
攻击者无法读取服务器发送的任何数据或修改cookie
值,根据同源策略。这意味着虽然
攻击者可以通过表单发送任何他想要的值,他将
无法修改或读取 cookie 中存储的值。自从
cookie值和form值必须相同,攻击者将
除非他能够猜测,否则无法成功提交表格
伪随机值。

这种方法的优点是不需要服务器状态;
您只需设置一次 cookie 值,然后每个 HTTP POST 都会检查
确保提交的值之一包含准确的值
相同的cookie值。两者之间的任何差异都意味着可能
XSRF 攻击。

From Jeff Atwood's blog entry:

Preventing CSRF and XSRF Attacks
(Oct 14, 2008)

The original post

The Felten and Zeller paper (pdf) recommends the "double-submitted
cookie" method to prevent XSRF:

When a user visits a site, the site should generate a
(cryptographically strong) pseudorandom value and set it as a
cookie on the user's machine. The site should require every form
submission to include this pseudorandom value as a form value and
also as a cookie value. When a POST request is sent to the site,
the request should only be considered valid if the form value and
the cookie value are the same. When an attacker submits a form on
behalf of a user, he can only modify the values of the form. An
attacker cannot read any data sent from the server or modify cookie
values, per the same-origin policy. This means that while an
attacker can send any value he wants with the form, he will be
unable to modify or read the value stored in the cookie. Since the
cookie value and the form value must be the same, the attacker will
be unable to successfully submit a form unless he is able to guess
the pseudorandom value.

The advantage of this approach is that it requires no server state;
you simply set the cookie value once, then every HTTP POST checks to
ensure that one of the submitted values contains the exact
same cookie value. Any difference between the two means a possible
XSRF attack.

嘿看小鸭子会跑 2024-11-06 23:03:53

该 cookie 是为了支持 AJAX。引用 Django 文档

虽然上述方法可用于 AJAX POST 请求,但它有一些不便:您必须记住在每个 POST 请求中将 CSRF 令牌作为 POST 数据传递。因此,有一种替代方法:在每个 XMLHttpRequest 上,将自定义 X-CSRFToken 标头设置为 CSRF 令牌的值。这通常更容易,因为许多 javascript 框架提供了允许在每个请求上设置标头的钩子。在 jQuery 中,您可以使用 ajaxSend 事件,如下所示:

$('html').ajaxSend(function(event, xhr, settings) {
    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
        // Only send the token to relative URLs i.e. locally.
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    }
});

将此添加到您网站中包含的 JavaScript 文件中将确保通过 jQuery 发出的 AJAX POST 请求不会被 CSRF 保护捕获。

The cookie is there for AJAX support. Quoting the Django docs:

While the above method can be used for AJAX POST requests, it has some inconveniences: you have to remember to pass the CSRF token in as POST data with every POST request. For this reason, there is an alternative method: on each XMLHttpRequest, set a custom X-CSRFToken header to the value of the CSRF token. This is often easier, because many javascript frameworks provide hooks that allow headers to be set on every request. In jQuery, you can use the ajaxSend event as follows:

$('html').ajaxSend(function(event, xhr, settings) {
    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
        // Only send the token to relative URLs i.e. locally.
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    }
});

Adding this to a javascript file that is included on your site will ensure that AJAX POST requests that are made via jQuery will not be caught by the CSRF protection.

伴梦长久 2024-11-06 23:03:53

他们发现了两个不同的问题。

Cookie 用于验证建立连接的客户端计算机。

隐藏的表单字段用于验证表单的来源。

示例场景:客户端计算机上的用户 A 可以为表单添加书签。用户 B 登录,从今天起获得有效的 cookie。当浏览器具有用户 B 会话中剩余的 cookie 时,用户 A 可以提交昨天的无效表单字段。


哪些客户端/浏览器资源通常会受到损害,

无。

这些 csrf 字段如何帮助我们免受伪造请求的侵害?

CSRF 令牌建立身份。

一台(且仅有一台)浏览器具有 CSRF cookie 令牌。但该浏览器可能会打开一个站点或添加书签的表单的多个副本。

该浏览器上的一个(且仅有一个)页面表单具有 CSRF 表单令牌。

浏览器和表单 cookie 必须匹配,以确保一个浏览器/一个表单。

They spot two different problems.

Cookie is to authenticate the client machine making the connection.

The hidden form field is to authenticate the source of the form.

Example Scenario: User A, on the client machine could bookmark the form. User B logs on, gets a valid cookie from today. User A could submit the invalid form field from yesterday when the browser has a left-over cookie from user B's session.


what client/browser resources are typically compromised,

None.

and how is it that these csrf fields help protect us from the forgery requests?

The CSRF tokens establish identity.

One (and only one) browser has a CSRF cookie token. But that browser could have multiple copies of a site open or bookmarked forms.

One (and only one) page form on that browser has a CSRF form token.

The browser and form cookies must match to assure one browser/one form.

半衾梦 2024-11-06 23:03:53

这是一个非常古老的问题,但我认为指出以下几点很重要。

在 Django 5.0.6(可能还有其他早期的 Django 版本)中,csrftoken cookie 和 csrfmiddleware 值(HTML 表单的隐藏输入)不相同。显然,这是为了防止 BREACH 攻击。

csrfmiddleware 作为 csrftoken 的函数和随机生成的掩码(在 get_token 函数中)进行计算,因此 csrfmiddleware code> 对于每个 GET 请求都是不同的,这意味着,每当您使用表单重新加载页面时,您都会得到不同的 csrfmiddleware,但您仍然应该拥有相同的 csrftoken >,尽管 csrftoken 也可能过期。您可以打开浏览器的开发者工具自行检查。

有关 Django 中 CSRF 保护如何工作的说明,请参阅官方文档,虽然一开始我觉得很混乱,所以你可能想寻找其他更好的解释。 源代码也可以帮助您了解详细信息。

This is a very old question, but I think it's important to point out the following.

In Django 5.0.6 (and possibly other previous Django versions), the csrftoken cookie and the csrfmiddleware value (of the HTML form's hidden input) are not the same. Apparently, this is to protect against BREACH attacks.

The csrfmiddleware is computed as a function of csrftoken and a randomly generated mask (in the get_token function), so the csrfmiddleware is different for each GET request, which means that, whenever you reload the page with the form, you will get a different csrfmiddleware, but you should still have the same csrftoken, although csrftoken can also expire. You can open your browser's developer tools and check for yourself.

An explanation of how CSRF protection works in Django can be found in the official documentation, although I found it confusing at the beginning, so you may want to look for other better explanations. The source code may also help you understand the details.

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