【发布时间】:2015-11-02 04:14:20
【问题描述】:
我是 AngularJS 和 Python Tornado 的新手,目前正在研究 CSRF/XSRF 检查。当我第一次向“test_c”发送 GET 请求时,我检查了“WebService.py”在标头中返回“set-cookie”,并且在我签入浏览器时确实创建了 cookie。但是当 POST 请求发送到“test”时,Tornado 会显示“POST 中缺少'_xsrf' 参数”错误...
在检查 POST 请求的标头后,我发现 xsrf cookie 是在标头中发送的,名称为 'cookie'(例如:Cookie:PHPSESSID=xxx; X-Csrftoken=xxx; csrftoken=xxx; _xsrf= xxx)。 tornado\web.py 中定义的 check_xsrf_cookie 函数无法正确获取 xsrf 令牌,因为该函数试图从 POST 的参数、名称为“X-Xsrftoken”或“X-Csrftoken”的标头中获取令牌。
因此,我添加了一些代码来检查标题中“cookie”中的 csrf 令牌,如下所示,它按预期工作......我想知道我是否以正确的方式解决了这个问题?或者 Tornado/AngularJS 已经用其他函数解决了这个问题,或者我只需要添加一些参数来使 csrf 令牌按照 Tornado 的预期发送?
===========================================
Tornado\Web.py
===========================================
def check_xsrf_cookie(self):
###### Added by me #####
_cookies_dict = {}
_cookies_header_reformat = re.findall(r'\w+=[\w\d.]+', self.request.headers.get('Cookie'))
for _cookie in _cookies_header_reformat:
key, value = _cookie.split('=', 1)
_cookies_dict[key] = value*
#########################
token = (self.get_argument("_xsrf", None) or
self.request.headers.get("X-Xsrftoken") or
self.request.headers.get("X-Csrftoken")
###### Added by me #####
or _cookies_dict['csrftoken'])
#########################
if not token:
raise HTTPError(403, "'_xsrf' argument missing from POST")
_, token, _ = self._decode_xsrf_token(token)
_, expected_token, _ = self._get_raw_xsrf_token()
if not _time_independent_equals(utf8(token), utf8(expected_token)):
raise HTTPError(403, "XSRF cookie does not match POST argument")
===========================================
WebService.py:
===========================================
class Basic(tornado.web.RequestHandler):
def set_default_headers(self):
self.set_header('Access-Control-Allow-Origin', self.request.headers.get('Origin', '*'))
self.set_header('Access-Control-Allow-Methods', 'GET, POST, DELETE, PUT, OPTIONS')
self.set_header('Access-Control-Allow-Credentials', 'true')
class test(Basic):
def get(self):
self.write('hi')
def put(self):
self.set_status(200)
def post(self):
print('ok')
def delete(self):
self.set_status(200)
class test_c(Basic):
def get(self):
self.set_cookie('_xsrf', '12345')
settings = {
"xsrf_cookies": True,
"debug": True,
}
application = tornado.web.Application([
(r"/test", test),
(r"/test_c", test_c),
], **settings)
===========================================
JavaScript.js:
===========================================
(function() {
angular.module('ngRouteExample', ['ngCookies'])
.config(function($httpProvider) {
$httpProvider.defaults.withCredentials = true;
})
.controller('MainController', function($http, $scope) {
$http.get('http://localhost:8889/test_c')
.success(function(headers, data) {
$http.post('http://localhost:8889/test')
.then(function() {
alert('!');
});
});
});
}) ();
编辑: 我删除了添加到 Tornado\web.py 的所有代码。相反,我在调用“test_c”时返回了 cookie 的值。并在从 JavaScript 发出 POST 请求时设置标头。但是当 POST 请求验证令牌时,出现“XSRF cookie 与 POST 参数不匹配”错误。
我检查了从 GET 请求返回、从 POST 请求发送并在触发“test_c”时打印的两个令牌都是“6e785017a6a1c28377a7d92187806136”。
但是当我从 Tornado\web.py 打印“token”和“expected_token”时,它们变成了不同的值...“token”显示为 b'nxP\x17\xa6\xa1\xc2\x83w\xa7 \xd9!\x87\x80a6' 和 "expected_token" 作为 b'\x11\xc4/\xa9\xd4\xe3\x83\xa2\xd9`\xc4\x12\xaf2\xfeK'...
===========================================
WebService.py
===========================================
class test_c(Basic):
def get(self):
if(self.get_cookie('X-Xsrftoken') == None):
self.set_cookie('X-Xsrftoken', hashlib.md5(str(time.localtime()).encode('utf8')).hexdigest())
print(type(self.get_cookie('X-Xsrftoken'))) # For Debug
print(self.get_cookie('X-Xsrftoken')) # For Debug
self.write(self.get_cookie('X-Xsrftoken'))
===========================================
JavaScript.js
===========================================
.controller('MainController', function($http, $scope) {
$http.get('http://localhost:8889/test_c')
.success(function(data) {
$http.post('http://localhost:8889/test', '1', {headers: {'X-Xsrftoken': data}})
.then(function() {
alert('!');
});
});
});
编辑 2 我深入研究了 Tornado\web.py 并找到了解决我在上次编辑中提到的问题的方法,但不确定它是否正确。如果有其他更好的方法,请告诉我。
在 Tornado\web.py 中,它尝试将来自 POST 参数或标头的 CSRF 令牌与它存储在“check_xsrf_cookie”函数中的 cookie 进行匹配。 Tornado 使用 "_get_raw_xsrf_token" 函数来获取名称为 "_xsrf" 但不是 "X-Xsrftoken" 或 "X-Csrftoken" Tornado 用于检查标头的 CSRF cookie。因此,我修改了“test_c”函数以生成名为“_xsrf”的 CSRF cookie 并将其返回到前端。并且“JavaScript.js”保持相同的方式在标题中以名称“X-Xsrftoken”发布令牌,因此 Tornado 可以在验证时检索它。
===========================================
WebService.py
===========================================
class test_c(Basic):
def get(self):
if(self.get_cookie('_xsrf') == None):
self.set_cookie('_xsrf', hashlib.md5(str(time.localtime()).encode('utf8')).hexdigest())
self.write(self.get_cookie('_xsrf').encode('utf8'))
===========================================
JavaScript.js
===========================================
.controller('MainController', function($http, $scope) {
$http.get('http://localhost:8889/test_c')
.success(function(data) {
$http.post('http://localhost:8889/test', '1', {headers: {'X-Xsrftoken': data}})
.then(function() {
alert('!');
});
});
});
【问题讨论】:
标签: python angularjs cookies tornado csrf