首页 文章

具有跨域的角度js应用中的CSRF保护

提问于
浏览
4

我的API在 api.domain.com 中运行,前端角度应用程序在 frontend.domain.com 下运行

CSRF implementation

  • 在我的 api.domain.com 上启用了CORS,只允许从 frontend.domain.com 访问 .

  • CSRF令牌在名为 X-CSRFTOKEN 的自定义响应头中设置

  • 在Angular应用程序中,首先它将发出获取CSRF令牌的请求,并将其附加到每个 post 请求的请求标头

//get CSRF token
$http.get("http://localhost:3000/").then(function (response) {

    console.log(response.headers('X-CSRFTOKEN'));

    var request = {
        method: "POST",
        url: 'http://localhost:3000/csrftest',
        headers: {
            "CSRF-Token": response.headers('X-CSRFTOKEN')
        }
    }
    //post request with CSRF token in header
    $http(request).then(function (res) {
        console.log(res);
    }, function (err) {
        console.log(err);
    })

}, function (err) {
    console.log("Error", err);
});

一个缺点是,我需要为每个请求获取CSRF令牌,因此每个请求都需要额外的 $http 调用,因为CSRF令牌正在更改每个请求(无论如何都要克服这个额外的http调用?)

我的问题是在跨域应用程序中添加CSRF保护的正确方法是什么?

1 回答

  • 2

    有没有办法克服这个额外的HTTP调用?

    在我看来(或者我认为是一般做法),在应用程序开始时,您应该进行一次CSRF调用,并且您的服务器应该设置一个包含CSRF令牌的cookie(因此您需要修改服务器代码以生成当前用户会话的单个CSRF令牌,应该对整个用户会话有效 .

    然后,在Angular应用程序中,定义请求拦截器,您可以读取该cookie并将其设置为每个POST请求的HTTP头 .

    在这里阅读更多:https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-header_token

    For example :(使用现有代码在响应头中传递令牌)

    假设您的服务器每次都为现有会话使用相同的CSRF令牌 .

    run 块中,点击API并获取令牌并将其存储在 localStorage 中:

    angular.module('myApp').run(function ($http) {
        // This will be executed once the application loads 
        $http.get('http://localhost:3000/').then(function (response) {
            // store the token in the local storage for further use
            localStorage.csrfToken = response.headers('X-CSRFTOKEN');
        });
    });
    

    然后添加一个拦截器并修改POST请求以添加该令牌:

    angular.module('myApp').config(function ($httpProvider) {
        $httpProvider.interceptors.push(function() {
            return {
                'request': function(config) {
                    if (config.method === 'POST') {
                        config.headers['CSRF-Token'] = localStorage.csrfToken;
                    }
    
                    return config;
                }
            };
        });
    });
    

    现在,您的每个POST请求都将被截获,并且将设置CSRF令牌 .

    我需要一个CSRF令牌的DRY代码

    但是如果你想要一个DRY代码,你想在每个POST请求之前进行额外的HTTP调用来获取CSRF令牌,那么也可以使用拦截器或服务 .

    使用拦截器:

    angular.module('myApp').config(function ($httpProvider) {
        $httpProvider.interceptors.push(function($http, $q) {
            function doAnotherCallToGetTokenAndSetItToRequest(config) {
                var defer = $q.defer();
    
                $http.get("http://localhost:3000/").then(function (response) {
                    config.headers['CSRF-Token'] = response.headers('X-CSRFTOKEN');
                    defer.resolve(config);
                });
    
                return defer.promise;
            }
    
            return {
                'request': function(config) {
                    if (config.method === 'POST') {
                        return doAnotherCallToGetTokenAndSetItToRequest(config);
                    }
    
                    return config;
                }
            };
        });
    });
    

    来自docs

    请求:使用http配置对象调用拦截器 . 该函数可以自由修改配置对象或创建新对象 . 该函数需要直接返回配置对象,或者包含配置或新配置对象的promise .

    现在,在代码中的任何地方,只需直接调用API,拦截器将负责提取新的CSRF令牌并将其设置为当前请求:

    var request = {
        method: "POST",
        url: 'http://localhost:3000/csrftest'
    }
    
    $http(request).then(function (res) {
        console.log(res);
    }, function (err) {
        console.log(err);
    })
    

相关问题