(Django) AJAX 请求的 CSRF 验证在 Chrome 但不是 Firefox 中工作
(Django) CSRF Verification for AJAX requests working in Chrome but not Firefox
如标题所述,我的 (Django) CSRF 验证在 Chrome 中有效,但在 Firefox 中无效,我想知道为什么,以便我可以解决此问题。
我将其包含在我的 base.html 文件的 head 标记中,我的应用程序中的所有其他文件都从该文件扩展:
base.html,头部标签底部
<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。这是回溯:
回溯
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
回应
<!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>
有什么问题吗?
已解决:我将 @ensure_csrf_cookie 放在获取 cookie 的视图上(不是 ajax 请求调用的函数——这让我感到困惑)。现在 Firefox 上不再有 403。耶
在您的请求中 headers,我看到:
X-CSRFToken null
所以我猜测是在 Firefox 中设置了 cookie。也许它已经从之前的 session.
设置在 Chrome 中
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().
尝试在您的 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) CSRF 验证在 Chrome 中有效,但在 Firefox 中无效,我想知道为什么,以便我可以解决此问题。
我将其包含在我的 base.html 文件的 head 标记中,我的应用程序中的所有其他文件都从该文件扩展:
base.html,头部标签底部
<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。这是回溯:
回溯
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
回应
<!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>
有什么问题吗?
已解决:我将 @ensure_csrf_cookie 放在获取 cookie 的视图上(不是 ajax 请求调用的函数——这让我感到困惑)。现在 Firefox 上不再有 403。耶
在您的请求中 headers,我看到:
X-CSRFToken null
所以我猜测是在 Firefox 中设置了 cookie。也许它已经从之前的 session.
设置在 Chrome 中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().
尝试在您的 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', {...})
我不确定这是否是根本问题,但我希望这对您有所帮助!