首页 文章

csrf使用AngularJS检查Tornado

提问于
浏览
0

我是AngularJS和Python Tornado的新手,目前正致力于CSRF / XSRF检查 . 我已经检查过“WebService.py”在 Headers 中返回“set-cookie”,而我第一次向“test_c”发送了一个GET请求,并且在我在浏览器中检查时创建了cookie . 但是,当POST请求发送到“test”时,Tornado会显示“POST中缺少'_xsrf'参数”错误...

检查POST请求的 Headers 后,我发现xsrf cookie在 Headers 中发送,名称为'cookie'(例如:Cookie:PHPSESSID = xxx; X-Csrftoken = xxx; csrftoken = xxx; _xsrf = xxx) . tornado \ web.py中定义的check_xsrf_cookie函数无法正确获取xsrf令牌,因为该函数试图从POST的参数,名称为“X-Xsrftoken”或“X-Csrftoken”的标头获取令牌 .

因此,我添加了一些代码来检查 Headers 中的'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('!');
                        });
                });
        });
}) ();

EDIT: 我删除了我添加到Tornado \ web.py的所有代码 . 相反,我在调用"test_c"时返回了cookie的值 . 并在从JavaScript发出POST请求时设置标头 . 但是当POST请求验证了toke时,我收到"XSRF cookie does not match POST argument"错误 .

我检查了从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('!');
                    });
            });
    });

EDIT 2 我挖到Tornado \ web.py并找到了解决我在上次编辑中提到的问题的方法,但不确定它是否正确 . 如果有任何其他更好的方法,请告诉我 .

在Tornado \ web.py中,它尝试将POST参数或 Headers 中的CSRF令牌与存储在“check_xsrf_cookie”函数中的cookie进行匹配 . 并且Tornado使用“_get_raw_xsrf_token”函数来获取名称为“_xsrf”的CSRF cookie,但不是“X-Xsrftoken”,也不是用于检查 Headers 的“X-Csrftoken”Tornado . 因此,我修改了我的“test_c”函数以生成名为“_xsrf”的CSRF cookie并将其返回到前端 . 并且“JavaScript.js”保持以相同的方式在 Headers 中使用名称“X-Xsrftoken”POST标记,因此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('!');
                    });
            });
    });

1 回答

  • 0

    CSRF保护的重点是以相同的两种方式发送相同的值:一次在cookie中(浏览器自动发送),一次在请求本身(主体或HTTP头)中 . 在CSRF攻击中,cookie是“只写”的:浏览器会将它们发送到服务器,但是攻击者无法分辨它们是什么 . 这将使攻击者充当经过身份验证的用户,因此为了防止这种情况,我们要求在请求中出现CSRF令牌(证明发出请求的页面具有读取令牌的能力) .

    根据您的更改,cookie将用于比较的两侧,完全取消检查 . 相反,您必须更改javascript端以在 X-Csrftoken HTTP标头中发送CSRF令牌(如果表单编码,则在POST正文中) .

相关问题