“CSRF 令牌丢失或不正确”而在 Django 中通过 AJAX 发布参数

发布于 2024-11-17 07:24:53 字数 450 浏览 4 评论 0原文

我尝试发布类似的参数

 jQuery.ajax(
        {
            'type': 'POST',
            'url': url,
            'contentType': 'application/json',
            'data': "{content:'xxx'}",
            'dataType': 'json',
            'success': rateReviewResult 
        }
    );

,但是,Django 返回 Forbidden 403. CSRF 验证失败。请求已中止。 我正在使用 'django.middleware.csrf.CsrfViewMiddleware' 并且找不到如何在不影响安全性的情况下防止此问题。

I try to post parameter like

 jQuery.ajax(
        {
            'type': 'POST',
            'url': url,
            'contentType': 'application/json',
            'data': "{content:'xxx'}",
            'dataType': 'json',
            'success': rateReviewResult 
        }
    );

However, Django return Forbidden 403. CSRF verification failed. Request aborted.
I am using 'django.middleware.csrf.CsrfViewMiddleware' and couldn't find how I can prevent this problem without compromising security.

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

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

发布评论

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

评论(14

顾忌 2024-11-24 07:24:53

您可以通过两种不同的方式发出 AJAX post 请求:

  1. 告诉您的视图不要检查 csrf 令牌。这可以通过使用装饰器 @csrf_exempt 来完成,如下所示:

    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt
    def your_view_name(请求):
        ...
    
  2. 要在每个 AJAX 请求中嵌入 csrf 令牌,对于 jQuery 可能是:

    <前><代码>$(函数 () {
    $.ajaxSetup({
    headers: { "X-CSRFToken": getCookie("csrftoken") }
    });
    });

    其中getCookie函数从cookie中检索csrf令牌。我使用以下实现:

    函数 getCookie(c_name)
    {
        if (document.cookie.length > 0)
        {
            c_start = document.cookie.indexOf(c_name + "=");
            if (c_start != -1)
            {
                c_start = c_start + c_name.length + 1;
                c_end = document.cookie.indexOf(";", c_start);
                if (c_end == -1) c_end = document.cookie.length;
                返回 unescape(document.cookie.substring(c_start,c_end));
            }
        }
        返回 ””;
     }
    

    此外,jQuery 有一个用于访问 cookie 的插件,类似的东西:

    //设置cookie
    $.cookie('cookiename', 'cookievalue');
    // 读取cookie
    var myCookie = $.cookie('cookiename');
    //删除cookie
    $.cookie('cookiename', null);
    

You can make AJAX post request in two different ways:

  1. To tell your view not to check the csrf token. This can be done by using decorator @csrf_exempt, like this:

    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt
    def your_view_name(request):
        ...
    
  2. To embed a csrf token in each AJAX request, for jQuery it may be:

    $(function () {
        $.ajaxSetup({
            headers: { "X-CSRFToken": getCookie("csrftoken") }
        });
    });
    

    Where the getCookie function retrieves csrf token from cookies. I use the following implementation:

    function getCookie(c_name)
    {
        if (document.cookie.length > 0)
        {
            c_start = document.cookie.indexOf(c_name + "=");
            if (c_start != -1)
            {
                c_start = c_start + c_name.length + 1;
                c_end = document.cookie.indexOf(";", c_start);
                if (c_end == -1) c_end = document.cookie.length;
                return unescape(document.cookie.substring(c_start,c_end));
            }
        }
        return "";
     }
    

    Also, jQuery has a plugin for accessing cookies, something like that:

    // set cookie
    $.cookie('cookiename', 'cookievalue');
    // read cookie
    var myCookie = $.cookie('cookiename');
    // delete cookie
    $.cookie('cookiename', null);
    
倚栏听风 2024-11-24 07:24:53

我发现的最简单的方法是在数据中包含 {{csrf_token}} 值:

jQuery.ajax(
    {
        'type': 'POST',
        'url': url,
        'contentType': 'application/json',
        'data': {
            'content': 'xxx',
            'csrfmiddlewaretoken': '{{ csrf_token }}',
        },
        'dataType': 'json',
        'success': rateReviewResult 
    }
);

The simplest way I have found is to include the {{csrf_token}} value in the data:

jQuery.ajax(
    {
        'type': 'POST',
        'url': url,
        'contentType': 'application/json',
        'data': {
            'content': 'xxx',
            'csrfmiddlewaretoken': '{{ csrf_token }}',
        },
        'dataType': 'json',
        'success': rateReviewResult 
    }
);
痕至 2024-11-24 07:24:53

我花了一段时间才明白如何处理代码丹尼尔发布的。但实际上您所要做的就是将其粘贴到 javascript 文件的开头。

对我来说,到目前为止最好的解决方案是:

  1. 创建一个csrf.js文件

  2. 粘贴csrf.js 中的代码 file

  3. 在模板中引用你需要的代码

    
    

请注意,STATIC_PREFIX/js/csrf.js 指向我的文件。我实际上正在使用 {% get_static_prefix as STATIC_PREFIX %} 加载 STATIC_PREFIX 变量。


高级提示:如果您使用模板并且有类似 base.html 的扩展源,那么您可以直接从那里引用脚本,而不必不用再担心你的其他文件了。据我了解,这也不应该代表任何安全问题。

It took me a while to understand what to do with the code that Daniel posted. But actually all you have to do is paste it at the beginning of the javascript file.

For me, the best solution so far is:

  1. Create a csrf.js file

  2. Paste the code in the csrf.js file

  3. Reference the code in the template you need it

    <script type="text/javascript" src="{{ STATIC_PREFIX }}js/csrf.js"></script>
    

Notice that STATIC_PREFIX/js/csrf.js points to my file. I am actually loading the STATIC_PREFIX variable with {% get_static_prefix as STATIC_PREFIX %}.


Advanced tip: if you are using templates and have something like base.html where you extend from, then you can just reference the script from there and you don't have to worry anymore in there rest of your files. As far as I understand, this shouldn't represent any security issue either.

她如夕阳 2024-11-24 07:24:53

简单而简短

$.ajaxSetup({
  headers: { "X-CSRFToken": '{{csrf_token}}' }
});

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
  beforeSend: function(xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", '{{csrf_token}}');
    }
  }
});

docs

Simple and short

$.ajaxSetup({
  headers: { "X-CSRFToken": '{{csrf_token}}' }
});

OR

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
  beforeSend: function(xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", '{{csrf_token}}');
    }
  }
});

docs

云巢 2024-11-24 07:24:53

由于缺乏直接的答案,您只需将标头 X-CSRFToken 添加到 cookie csrftoken 中的 ajax 请求即可。如果没有 插件,JQuery 不会执行 cookie(出于某种原因),因此:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>

最小的代码更改是:

$.ajax({
  headers: { "X-CSRFToken": $.cookie("csrftoken") },
  ...
});

For lack of a straight forward answer, you just have to add the header X-CSRFToken to the ajax request which is in the cookie csrftoken. JQuery doesn't do cookies (for some reason) without a plugin so:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>

and the minimal code change is:

$.ajax({
  headers: { "X-CSRFToken": $.cookie("csrftoken") },
  ...
});
〆凄凉。 2024-11-24 07:24:53

如果您不将 js 嵌入到模板中,则无需任何插件的最快解决方案是:

,然后将 csrfmiddlewaretoken 添加到您的 data 字典中:

$.ajax({
            type: 'POST',
            url: somepathname + "do_it/",
            data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
            success: function() {
                console.log("Success!");
            }
        })

如果您确实将 js 嵌入到模板,很简单: data: {csrfmiddlewaretoken: '{{ csrf_token }}'}

The fastest solution without any plugins if you are not embedding js into your template is:

Put <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script> before your reference to script.js file in your template, then add csrfmiddlewaretoken into your data dictionary:

$.ajax({
            type: 'POST',
            url: somepathname + "do_it/",
            data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
            success: function() {
                console.log("Success!");
            }
        })

If you do embed your js into the template, it's as simple as: data: {csrfmiddlewaretoken: '{{ csrf_token }}'}

回忆躺在深渊里 2024-11-24 07:24:53

我昨天遇到了同样的问题,认为如果有一种简单的方法来处理它,会对人们有所帮助,所以我为此编写了一个 jQuery 插件: jquery.djangocsrf。它不是在每个请求中添加 CSRF 令牌,而是将自身挂接到 AjaxSend jQuery 事件上,并将客户端 cookie 添加到标头中。

使用方法如下:

1- 包含它:

<script src="path/to/jquery.js"></script>
<script src="path/to/jquery.cookie.js"></script>
<script src="path/to/jquery.djangocsrf.js"></script>

2- 在代码中启用它:

$.djangocsrf( "enable" );

如果您的模板使用 {% csrf_token %},Django 总是在 cookie 中添加令牌。为了确保即使您不在模板中使用特殊标记,它也始终会添加它,请使用 @ensure_csrf_cookie 装饰器:

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def my_view(request):
    return render(request, 'mytemplate.html')

注意:我使用的是 Django 1.6.2。

I got the same issue yesterday and thought it would help people if there were a simple way to handle it, so I wrote a jQuery plugin for that: jquery.djangocsrf. Instead of adding the CSRF token in every request, it hooks itself on the AjaxSend jQuery event and adds the client cookie in a header.

Here’s how to use it:

1- include it:

<script src="path/to/jquery.js"></script>
<script src="path/to/jquery.cookie.js"></script>
<script src="path/to/jquery.djangocsrf.js"></script>

2- enable it in your code:

$.djangocsrf( "enable" );

Django always add the token in a cookie if your template uses {% csrf_token %}. To ensure it always adds it even if you don’t use the special tag in your template, use the @ensure_csrf_cookie decorator:

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def my_view(request):
    return render(request, 'mytemplate.html')

Note: I’m using Django 1.6.2.

梦巷 2024-11-24 07:24:53

谢谢大家的所有回答。我正在使用 Django 1.5.1。我参加聚会有点晚了,但还是到了。

我发现 Django 项目 的链接非常有用,但是我真的不想每次想要进行 Ajax 调用时都必须包含额外的 JavaScript 代码。

我喜欢 jerrykan 的响应,因为它非常简洁,只在正常的 Ajax 调用中添加一行。为了回应下面关于 Django 模板标签不可用的情况的评论,从 DOM 加载 csrfmiddlewaretoken 怎么样?

var token = $('input[name="csrfmiddlewaretoken"]').prop('value');
jQuery.ajax({
    type: 'POST',
    url: url,
    data: { 'csrfmiddlewaretoken': token },
    dataType: 'json',
    success: function(data) { console.log('Yippee! ' + data); } 
});

编辑 2016 年 3 月

在过去的几年里,我对这个问题的处理方法已经发生了变化。我将下面的代码(来自 Django 文档)添加到 main .js 文件并将其加载到每个页面上。完成后,您就不必再担心 ajax 的 CSRF 令牌了。

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;
}
var csrftoken = getCookie('csrftoken');

Thank you everyone for all the answers. I am using Django 1.5.1. I'm a little late to the party, but here goes.

I found the link to the Django project to be very useful, but I didn't really want to have to include the extra JavaScript code every time I wanted to make an Ajax call.

I like jerrykan's response as it is very succinct and only adds one line to an otherwise normal Ajax call. In response to the comments below his comment regarding situations when Django template tags are unavailable, how about loading up the csrfmiddlewaretoken from the DOM?

var token = $('input[name="csrfmiddlewaretoken"]').prop('value');
jQuery.ajax({
    type: 'POST',
    url: url,
    data: { 'csrfmiddlewaretoken': token },
    dataType: 'json',
    success: function(data) { console.log('Yippee! ' + data); } 
});

EDIT March 2016

My approach to this issue over the past few years has changed. I add the code below (from the Django docs) to a main.js file and load it on every page. Once done, you shouldn't need to worry about the CSRF token with ajax again.

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;
}
var csrftoken = getCookie('csrftoken');

蓬勃野心 2024-11-24 07:24:53

在请求中包含 x-csrftoken 标头:

var token = $('input[name="csrfmiddlewaretoken"]').prop('value');
jQuery.ajax({
    type: 'POST',
    url: url,
    beforeSend : function(jqXHR, settings) {
        jqXHR.setRequestHeader("x-csrftoken", get_the_csrf_token_from_cookie());
    },
    data: data,
    dataType: 'json',

});

Include x-csrftoken header in request:

var token = $('input[name="csrfmiddlewaretoken"]').prop('value');
jQuery.ajax({
    type: 'POST',
    url: url,
    beforeSend : function(jqXHR, settings) {
        jqXHR.setRequestHeader("x-csrftoken", get_the_csrf_token_from_cookie());
    },
    data: data,
    dataType: 'json',

});
享受孤独 2024-11-24 07:24:53

如果在阅读其他答案后,有人仍然在挣扎,请尝试以下操作:

   $.ajax({
            type: "POST",
            beforeSend: function (request)
            {
                request.setRequestHeader("X-CSRF-TOKEN", "${_csrf.token}");
            },
            url: servlet_path,
            data : data,
            success : function(result) {
            console.log("Success!");
   }
});

If, after reading other answers, someone is still struggling please try this:

   $.ajax({
            type: "POST",
            beforeSend: function (request)
            {
                request.setRequestHeader("X-CSRF-TOKEN", "${_csrf.token}");
            },
            url: servlet_path,
            data : data,
            success : function(result) {
            console.log("Success!");
   }
});
遗忘曾经 2024-11-24 07:24:53

请注意,以这种方式执行此操作时,请确保

标记内没有 {% csrf_token %}。然后按照解释 此处 将以下代码添加到您的 JavaScript 中

    function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // 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;
    }
    const csrftoken = getCookie('csrftoken');

// using js fetch
// https://docs.djangoproject.com/en/3.1/ref/csrf/#setting-the-token-on-the-ajax-request
    const request = new Request(
    /* URL */,
    {headers: {'X-CSRFToken': csrftoken}}
);
fetch(request, {
    method: 'POST',
    mode: 'same-origin'  // Do not send CSRF token to another domain.
}).then(function(response) {
    // ...
});

Please not that when doing it this way make sure you don't have the {% csrf_token %} inside the <form></form> tags. Then as explained here add the following code to your javascript

    function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // 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;
    }
    const csrftoken = getCookie('csrftoken');

// using js fetch
// https://docs.djangoproject.com/en/3.1/ref/csrf/#setting-the-token-on-the-ajax-request
    const request = new Request(
    /* URL */,
    {headers: {'X-CSRFToken': csrftoken}}
);
fetch(request, {
    method: 'POST',
    mode: 'same-origin'  // Do not send CSRF token to another domain.
}).then(function(response) {
    // ...
});
金兰素衣 2024-11-24 07:24:53

只是想在这里指出,如果 GET 在您的用例中有效,那么它就不需要 CSRF 令牌。对于我的用例,使用 GET 就可以了。

Just want to put it out here that if GET works in your use case, then it wouldn't need the CSRF token. For my use case, using GET was OK.

情何以堪。 2024-11-24 07:24:53

html

<form action="">
        {% csrf_token %}
    </form>

JS

 <script>   
     const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
              const request = new Request(
                   'url_here',
                  {headers: {'X-CSRFToken': csrftoken}}
              );
              fetch(request, {
                  method: 'POST',
                  // mode: 'same-origin' optinal // Do not send CSRF token to another domain.
              }).then(function(response) {
                  console.log(response);
              });
    
    </script>

更多详细信息的参考链接

html

<form action="">
        {% csrf_token %}
    </form>

JS

 <script>   
     const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
              const request = new Request(
                   'url_here',
                  {headers: {'X-CSRFToken': csrftoken}}
              );
              fetch(request, {
                  method: 'POST',
                  // mode: 'same-origin' optinal // Do not send CSRF token to another domain.
              }).then(function(response) {
                  console.log(response);
              });
    
    </script>

reference link for more detail

嘿哥们儿 2024-11-24 07:24:53

作为我的错误的总结:

  1. 不要忘记设置请求内容类型。
  2. 获取 csrf 值,可以从

    <块引用>

    注意。默认 cookie 名称为 csrftoken,但可以通过 CSRF_COOKIE_NAME 设置覆盖。

    • DOM,如果您无法访问 Cookie(您将 CSRF_USE_SESSIONS 或 CSRF_COOKIE_HTTPONLY 设置为 True)

    <块引用>

    document.querySelector('[name=csrfmiddlewaretoken]').value;

  3. 设置请求头,我使用的是XMLHttpRequest
const Http = new XMLHttpRequest();
Http.setRequestHeader("X-CSRFToken", CSRF_VALUE);
Http.setRequestHeader("X_CSRFTOKEN", CSRF_VALUE);

标头名称由 CSRF_HEADER_NAME 设置管理,默认为 HTTP_X_CSRFTOKEN

但是:“从服务器接收到的标头名称通过将所有字符转换为大写、用下划线替换所有连字符以及在名称中添加 'HTTP_' 前缀来规范化” src

因此,如果您设置 HTTP_X_CSRFTOKEN 标头,Django 会将其转换为 HTTP_HTTP_X_CSRFTOKEN ,这是不正确的名称,并会引发 CSRF Missed 错误。

Http.setRequestHeader("X-CSRFToken", csrftoken);        // This worked
Http.setRequestHeader("X-CSRFTOKEN", csrftoken);        // Also this
Http.setRequestHeader("HTTP-X-CSRFToken", csrftoken);   // Not working
Http.setRequestHeader("HTTP_X_CSRFTOKEN", csrftoken);   // Not Working
Http.setRequestHeader("X_CSRFTOKEN", csrftoken);        // Not working !!
  1. 不要在ajax中使用与浏览器不同的url 127.0.0.1 & localhost 不一样!!

  2. 据我所知,无需在请求正文中设置 data.append("csrfmiddlewaretoken", csrftoken);

As a summary for my mistakes:

  1. Don't forget to set the request content type.
  2. Get the csrf value, either from

    NB. The default cookie name is csrftoken, but can be overriden by CSRF_COOKIE_NAME setting.

    • The DOM, if you can't access the cookie (you set CSRF_USE_SESSIONS or CSRF_COOKIE_HTTPONLY to True)

    document.querySelector('[name=csrfmiddlewaretoken]').value;

  3. Set the request header, I'am using XMLHttpRequest
const Http = new XMLHttpRequest();
Http.setRequestHeader("X-CSRFToken", CSRF_VALUE);
Http.setRequestHeader("X_CSRFTOKEN", CSRF_VALUE);

The header name is managed by CSRF_HEADER_NAME setting, which its default is HTTP_X_CSRFTOKEN.

But: "The header name received from the server is normalized by converting all characters to uppercase, replacing any hyphens with underscores, and adding an 'HTTP_' prefix to the name" src.

So, If you set the HTTP_X_CSRFTOKEN header, Django will convert it to HTTP_HTTP_X_CSRFTOKEN which wis incorrect name and will raise CSRF missed error.

Http.setRequestHeader("X-CSRFToken", csrftoken);        // This worked
Http.setRequestHeader("X-CSRFTOKEN", csrftoken);        // Also this
Http.setRequestHeader("HTTP-X-CSRFToken", csrftoken);   // Not working
Http.setRequestHeader("HTTP_X_CSRFTOKEN", csrftoken);   // Not Working
Http.setRequestHeader("X_CSRFTOKEN", csrftoken);        // Not working !!
  1. Don't use url in ajax that is different than that of the browser 127.0.0.1 & localhost are not the same !!

  2. No need to set data.append("csrfmiddlewaretoken", csrftoken); in the request body, As I know.

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