django-1.11.3/angular-1.6.4:POST AJAX CSRF验证失败 [英] django-1.11.3/angular-1.6.4: POST AJAX CSRF verification failed
问题描述
我最近一直在研究AngularJS和Django.AngularJS承诺处理名为"XSRF-TOKEN"的传入cookie,并自动添加名为"X-XSRF-TOKEN"的请求标头.所以我在settings.py中设置了django变量:
CSRF_COOKIE_NAME ='XSRF-TOKEN'CSRF_HEADER_NAME ='X-CSRF-TOKEN'
很明显,它不能按
解决方案:
编辑
settings.py
,设置CSRF_HEADER_NAME ='HTTP_X_CSRF_TOKEN'
我如何找到解决方案:
django CSRF保护在/usr/local/lib/python2.7/dist-packages/django/middleware/csrf.py
中实现我在其中找到函数 process_view
的位置抛出了 CSRF令牌丢失或不正确的异常
:
request_csrf_token ="如果request.method =="POST":尝试:request_csrf_token = request.POST.get('csrfmiddlewaretoken','')除了IOError:经过#< TEST>打印('[STAGE-1] request_csrf_token =',request_csrf_token,_unsalt_cipher_token(request_csrf_token)))#</TEST>如果request_csrf_token ==":request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME,'')#< TEST>打印('[STAGE-2] request_csrf_token =',request_csrf_token,_unsalt_cipher_token(request_csrf_token)))打印('request.META:')对于k,v在request.META.items()中:print('',k,v)#</TEST>request_csrf_token = _sanitize_token(request_csrf_token)#< TEST>打印('[STAGE-3] request_csrf_token =',request_csrf_token,_unsalt_cipher_token(request_csrf_token)))打印('csrf_token =',csrf_token,_unsalt_cipher_token(csrf_token))#</TEST>如果不是_compare_salted_tokens(request_csrf_token,csrf_token):返回self._reject(请求,REASON_BAD_TOKEN)
如图所示,我插入了几行内容来帮助确定此函数中发生的事情.
令我惊讶的是,当我签出/var/log/apache2/error.log
来检索Django输出时,我发现 request_csrf_token
在两个STAGE-中都是空字符串1和STAGE-2,也就是 request.POST.get('csrfmiddlewaretoken','')
和 request.META.get(settings.CSRF_HEADER_NAME,'')
request_csrf_token
没有值.
因此,我签出了 request.META
词典BOOM,这是一个名为 HTTP_X_CSRF_TOKEN
的条目,它具有我想要的有效CSRF令牌.
显然django有这个BUG,您无法按照承诺自定义CSRF令牌HTTP请求标头名称( CSRF_HEADER_NAME
)的名称.django也未能保持其文档与代码的一致性,因为 CSRF_HEADER_NAME
的默认值在
I've been studying about both AngularJS and Django recently. AngularJS promises to handle incoming cookie named 'XSRF-TOKEN' and to add a request header named 'X-XSRF-TOKEN' automatically. So I set django variables in settings.py:
CSRF_COOKIE_NAME = 'XSRF-TOKEN'
CSRF_HEADER_NAME = 'X-CSRF-TOKEN'
Obviously, it is not working as is implied here. So I had to install angular-cookies.js module to retrieve 'csrftoken' cookie and put its value in request header 'X-CSRF-TOKEN'. However, it's still not working. So what ever it is this time, it must be on server side.
HTML template:
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
<title>youtube-dl</title>
<link href="/video/css/video.css" rel="stylesheet" type="text/css">
<script src="/js/angular-1.6.4.min.js" type="text/javascript"></script>
<script src="/js/angular-cookies.js" type="text/javascript"></script>
</head>
<body>
<div id="container" class="center" ng-app="vc" ng-controller="vc-ctl">
<img id="dl-icon" src="/video/logo.png"></img>
<form name="form">
{% csrftoken %}
<input class="url {{urlState}}" type="url" name="url" placeholder="https://" required autofocus ng-model="url">
<button class="btn btn1" ng-if="!form.url.$valid">
{{ buttonText }}
</button>
<button class="btn btn2 {{btn2State}}" ng-if="form.url.$valid" ng-click="click();">
{{ buttonText }}
</button>
</form>
</div>
<script src="/video/js/app.js" type="text/javascript"></script>
</body>
</html>
views.py:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import json
from django.shortcuts import render
from django.http import HttpResponse
from .models import VideoTask
# Create your views here.
def index(request):
if request.method == 'GET':
return render(request, 'video/index.html')
stat = {
"full_path: ", request.get_full_path(),
"host: ", request.get_host(),
"port: ", request.get_port(),
"is_ajax: ", request.is_ajax(),
"is_secure: ", request.is_secure(),
"data: ", request.read(),
}
return HttpResponse(json.dumps(stat))
settings.py:
CSRF_COOKIE_NAME = 'csrftoken'
CSRF_HEADER_NAME = 'X-CSRF-TOKEN'
app.js:
var app = angular.module("vc", [
'ngCookies'
]);
app.run(function($rootScope, $http, $cookies) {
$rootScope.buttonText = "Submit URL";
$http.defaults.headers.post['X-CSRF-TOKEN'] = $cookies.csrftoken;
});
app.controller("vc-ctl", function($scope, $http, $cookies) {
$scope.cb_success = function(res) {
$scope.progList = res.data.progList;
};
$scope.cb_failure = function(res) {
console.log(res.status);
};
$scope.postUrl = "/wsgi/video/";
$scope.click = function() {
//$scope.btn2State = "hidden";
var data = encodeURIComponent($scope.url);
var cookie_csrf = $cookies.get('csrftoken');
console.log("Cookie: \"" + cookie_csrf + "\"");
console.log("POST {data: \"" + data + "\"}");
var req = {
method: 'POST',
url: $scope.postUrl,
headers: {
'X-CSRF-TOKEN': cookie_csrf,
},
data: {
url: data
}
};
$http(req).then($scope.cb_success, $scope.cb_failure);
};
$scope.update = function() {
$http.post($scope.postUrl, {"action": "echo"}).then($scope.cb_success, $scope.cb_failure);
};
});
Obviously I have {% csrftoken %} in my HTML form. When I check my data parcels in Firefox, I have both headers, 'Cookie' and 'X-CSRF-TOKEN'.
Error page screenshot:
Solution:
Edit
settings.py
, setCSRF_HEADER_NAME = 'HTTP_X_CSRF_TOKEN'
How I find the solution:
django CSRF protection is implemented in /usr/local/lib/python2.7/dist-packages/django/middleware/csrf.py
where I located the function process_view
throwed the exception CSRF token missing or incorrect
:
request_csrf_token = ""
if request.method == "POST":
try:
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
except IOError:
pass
# <TEST>
print( '[STAGE - 1] request_csrf_token=', request_csrf_token, _unsalt_cipher_token(request_csrf_token) )
# </TEST>
if request_csrf_token == "":
request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')
# <TEST>
print( '[STAGE - 2] request_csrf_token=', request_csrf_token, _unsalt_cipher_token(request_csrf_token) )
print('request.META:')
for k, v in request.META.items():
print('', k, v)
# </TEST>
request_csrf_token = _sanitize_token(request_csrf_token)
# <TEST>
print( '[STAGE - 3] request_csrf_token=', request_csrf_token, _unsalt_cipher_token(request_csrf_token) )
print( 'csrf_token=', csrf_token, _unsalt_cipher_token(csrf_token) )
# </TEST>
if not _compare_salted_tokens(request_csrf_token, csrf_token):
return self._reject(request, REASON_BAD_TOKEN)
As is shown, I inserted a few lines to help determine what's going on in this function.
To my supprise, when I check out /var/log/apache2/error.log
to retrieve django output, I found request_csrf_token
is empty string in both STAGE-1 and STAGE-2, aka, request.POST.get('csrfmiddlewaretoken', '')
and request.META.get(settings.CSRF_HEADER_NAME, '')
had no value for request_csrf_token
.
So I checked out request.META
dictionary, BOOM, an entry named HTTP_X_CSRF_TOKEN
has exactly what I want, a valid CSRF token.
Obviously django has this BUG that you can't customize the name of CSRF token HTTP request header name (CSRF_HEADER_NAME
) as is promised. django also failed to keep its document compliance with its code, because the so-called default value of CSRF_HEADER_NAME
is HTTP_X_CSRFTOKEN
in its document.
这篇关于django-1.11.3/angular-1.6.4:POST AJAX CSRF验证失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!