(Django) AJAX 请求的 CSRF 验证在 Chrome 而不是 Firefox 中工作

Posted

技术标签:

【中文标题】(Django) AJAX 请求的 CSRF 验证在 Chrome 而不是 Firefox 中工作【英文标题】:(Django) CSRF Verification for AJAX requests working in Chrome but not Firefox 【发布时间】:2016-01-18 10:00:45 【问题描述】:

正如标题所述,我的 (Django) CSRF 验证在 Chrome 中有效,但在 Firefox 中无效,我想知道为什么我可以解决这个问题。

我将它包含在我的 base.html 文件的 head 标记中,我的应用程序中的所有其他文件都从该标记中扩展:

base.html,head 标签的底部

    <script>
    $(document).ready(function() 

        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');
        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", csrftoken);
                
            
        );
    );
    </script>

我在一个名为 browse.js 的文件中有这段代码,它需要向我自己的服务器发出 ajax 请求。

browse.js

Template = 
  setup : function()
    Template.events.csrf();
    // etc. etc.
  ,
  events: 
    csrf : function()
      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');
      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", csrftoken);
          
        
      );
    ,
//etc. etc.

//The actual ajax request
Data = 
  api : 
    ajax_get_listings : function(cb)
      var g, i, o, _ref;
      _ref = [
        $('#ci').val(), 
        $('#co').val(), 
        $('#guests').val()], 
        i = _ref[0], 
        o = _ref[1], 
        g = _ref[2];
      if (g) 
        console.log('getting listings');
        return $.ajax(
          url:'/api/get_listing_items/', 
          type: 'POST',
          datatype:'json',
          data: 
            available_start_date: i,
            available_end_date: o,
            max_guests: g
          , 
          success: function(d) 
            if (d.listings !== null)
              Data.listings._results = [];
              console.log(d);
              var l = $.parseJSON(
                $("<textarea/>").html(d.listings).text());
              console.log(l);
              data = l;
              console.log(data);
              return cb(data);
            else 
              $('#ct').text('No listings found for your search criteria. Please keep searching!');
            
          ,
        );
      
    ,
  ,
//etc. etc

同样,这在 Chrome 中运行良好。当我在 Firefox 上时,它只会给我一个 403 Forbidden。这是回溯:

追溯

标题

view source
Content-Type    
text/html
Date    
Mon, 19 Oct 2015 22:06:07 GMT
Server  
WSGIServer/0.1 Python/2.7.3
Vary    
Cookie
X-Frame-Options 
SAMEORIGIN
view source
Accept  
*/*
Accept-Encoding 
gzip, deflate
Accept-Language 
en-US,en;q=0.5
Cache-Control   
no-cache
Connection  
keep-alive
Content-Length  
54
Content-Type    
application/x-www-form-urlencoded; charset=UTF-8
Cookie  
_ga=GA1.1.1619904474.1445292335; _gat=1; TawkConnectionTime=0;      __tawkuuid=e||127.0.0.1||mnW1PFpM4y26O8w
+2HatshrE3nWV4w3xD7SAtEMYGtV647bMojOwsqzNlPdxYCdB||2;     Tawk_560d98fcc096ea637ec4b8c0=vs15.tawk.to:443
||0
DNT 
1
Host    
127.0.0.1:8008
Pragma  
no-cache
Referer 
http://127.0.0.1:8008/properties/
User-Agent  
Mozilla/5.0 (Windows NT 6.1; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0
X-CSRFToken 
null
X-Requested-With    
XMLHttpRequest  

响应

<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE">
  <title>403 Forbidden</title>
  <style type="text/css">
    html *  padding:0; margin:0; 
    body *  padding:10px 20px; 
    body * *  padding:0; 
    body  font:small sans-serif; background:#eee; 
    body>div  border-bottom:1px solid #ddd; 
    h1  font-weight:normal; margin-bottom:.4em; 
    h1 span  font-size:60%; color:#666; font-weight:normal; 
    #info  background:#f6f6f6; 
    #info ul  margin: 0.5em 4em; 
    #info p, #summary p  padding-top:10px; 
    #summary  background: #ffc; 
    #explanation  background:#eee; border-bottom: 0px none; 
  </style>
</head>
<body>
<div id="summary">
  <h1>Forbidden <span>(403)</span></h1>
  <p>CSRF verification failed. Request aborted.</p>


  <p>You are seeing this message because this site requires a CSRF cookie     when submitting forms.       This
 cookie is required for security reasons, to ensure that your browser is not     being hijacked by third
 parties.</p>
      <p>If you have configured your browser to disable cookies, please re-enable them, at least for this
 site, or for &#39;same-origin&#39; requests.</p>

</div>

<div id="info">
  <h2>Help</h2>
    <p>Reason given for failure:</p>
    <pre>
      CSRF cookie not set.
    </pre>
    <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
  <a
  href='http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ref-contrib-   csrf'>Django's
  CSRF mechanism</a> has not been used correctly.  For POST forms, you need to
  ensure:</p>

      <ul>
      <li>Your browser is accepting cookies.</li>

    <li>The view function uses <a
     href='http://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-  context-requestcontext'
><code>RequestContext</code></a>
    for the template, instead of <code>Context</code>.</li>

    <li>In the template, there is a <code>% csrf_token
%</code> template tag inside each POST form that
targets an internal URL.</li>

   <li>If you are not using <code>CsrfViewMiddleware</code>, then you must   use <code>csrf_protect</code> on any views that use the <code>csrf_token</code>
template tag, as well as those that accept the POST data.</li>
</ul><p>You're seeing the help section of this page because you have <code>DEBUG =
  True</code> in your Django settings file. Change that to   <code>False</code>,
 and only the initial error message will be displayed.  </p>

   <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
</div>

</body>
</html>

可能出了什么问题?


已解决:我将 @ensure_csrf_cookie 放在获取 cookie 的视图上(不是 ajax 请求调用的函数——这让我感到困惑)。现在 Firefox 上不再出现 403。耶

【问题讨论】:

【参考方案1】:

在您的请求标头中,我看到:

X-CSRFToken null

所以我的猜测是 cookie 是在 Firefox 中设置的。也许它已经在之前的会话中在 Chrome 中设置了。

The Django docs explain one reason why this may be:

警告

如果您的视图未呈现包含 csrf_token 的模板 模板标签,Django 可能不会设置 CSRF 令牌 cookie。这是 在表单被动态添加到页面的情况下很常见。到 针对这种情况,Django 提供了一个视图装饰器,它强制 cookie的设置:ensure_csrf_cookie()。

尝试在您的views.py 中导入ensure_csrf_cookie 装饰器并用它包装您的基础视图。例如:

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def base_view(request):
    # do stuff
    return render('base.html', ...)

我不确定这是否是根本问题,但我希望这会有所帮助!

【讨论】:

以上是关于(Django) AJAX 请求的 CSRF 验证在 Chrome 而不是 Firefox 中工作的主要内容,如果未能解决你的问题,请参考以下文章

错误:“CSRF 验证失败。请求中止。”在 Django 中使用 jquery ajax 时

Django(十六)基于模板的登录案例:登录装饰器csrf攻击方式及防护ajax的Post 的csrf开启写法生成验证码加验证码登录反向解析+传参

带有 django 的 JQuery AJAX 获取 csrf 错误 403

django 页面进行ajax post提交时,页面要添加{% csrf_token %}

django 页面进行ajax post提交时,页面要添加{% csrf_token %}

django 页面进行ajax post提交时,页面要添加{% csrf_token %}