简体   繁体   中英

CSRF protection in angular js application with cross domain

My APIs are running in api.domain.com and frontend angular application is running under frontend.domain.com

CSRF implementation

  1. Enabled CORS on my api.domain.com and access allowed only from frontend.domain.com .
  2. CSRF token is set in a custom response header called X-CSRFTOKEN
  3. In Angular app, first it will make a request to get the CSRF token and attach it with the request header for every post request

    //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); });

One drawback is, I need to get CSRF token for every request so there is an extra $http call needed for every request because the CSRF token is changing every request (is there anyway to overcome this extra http call?)

My question is it the proper way to adding the CSRF protection in Cross-domain applications??

Is there any way to overcome this extra HTTP call?

In my view (or as I saw as a general practice), at the start of the application, you should do the CSRF call once and your server should set a cookie containing the CSRF token (so you need to modify your server code to generate a single CSRF token for the current user session) which should be valid for the whole user session.

Then, in your Angular application, define the request interceptor, and you can read that cookie and set it as HTTP header for every POST request.

Read more here: https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-header_token

For example : (using your existing code of passing the token in response header)

Assuming, your server uses the same CSRF token everytime for the existing session.

In your run block, hit the API and get the token and store that in the 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');
    });
});

Then add an interceptor and modify the POST requests to add that token:

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;
            }
        };
    });
});

Now your every POST request will be intercepted and the CSRF token will be set.

I need a DRY code for CSRF token

But if you want a DRY code where you want to do an extra HTTP call for getting CSRF token before every POST request, that is also possible either using interceptors or a service.

Using an interceptor:

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;
            }
        };
    });
});

From the docs :

request : interceptors get called with a http config object. The function is free to modify the config object or create a new one. The function needs to return the config object directly, or a promise containing the config or a new config object.

Now, anywhere in your code, just call API's directly and the interceptor will take care of pulling a new CSRF token and setting it to the current request:

var request = {
    method: "POST",
    url: 'http://localhost:3000/csrftest'
}

$http(request).then(function (res) {
    console.log(res);
}, function (err) {
    console.log(err);
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM