As the title states, my (Django) CSRF verification is working in Chrome but not Firefox and I'd like to know why so I can fix this.
I have this included in the head tag of my base.html file from which all other files in my application extend:
base.html, bottom of the head tag
<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>
And I have this code in a file called browse.js which needs to make ajax requests to my own server.
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
}
Again, this works fine in Chrome. It only gives me a 403 Forbidden when I am on Firefox. Here is the traceback:
Traceback
Headers
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
Response
<!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 'same-origin' 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>
What could be wrong?
SOLVED: I put @ensure_csrf_cookie on the view that was getting the cookie (NOT the function the ajax request was calling -- this had confused me). No more 403s on Firefox now. Yay
In your request headers, I see:
X-CSRFToken null
So my guess is that the cookie is being set in Firefox. Perhaps it was already set in Chrome from a previous session.
The Django docs explain one reason why this may be:
Warning
If your view is not rendering a template containing the csrf_token template tag, Django might not set the CSRF token cookie. This is common in cases where forms are dynamically added to the page. To address this case, Django provides a view decorator which forces setting of the cookie: ensure_csrf_cookie().
Try importing the ensure_csrf_cookie
decorator in your views.py and wrapping your base view with it. Ex:
from django.views.decorators.csrf import ensure_csrf_cookie
@ensure_csrf_cookie
def base_view(request):
# do stuff
return render('base.html', {...})
I'm not sure if this is the root issue, but I hope this helps!
来源:https://stackoverflow.com/questions/33224870/django-csrf-verification-for-ajax-requests-working-in-chrome-but-not-firefox