how to implement csrf protection for cross domain requests
Asked Answered
P

3

21

I have two web apps, one for the Web UI in AngularJS and one for the REST webservices in Java. Both are deployed on separate domains.

The applications uses cookie for authentication. Whenever user enters a valid username and password, server returns a http only cookie back containing the token and that cookie is passed across all requests. I have enabled CORS on both apps, thats why the session cookie is working properly.

Now, I am trying to add CSRF protection for this. I was trying to use the csrf cookie where in the server will send the csrf cookie(not httponly) as part of REST response and the UI will read the value from the cookie and pass that in a csrf token header for the other REST calls.

The problem with this approach I am facing is that since the server is in different domain, I cannot read the cookie using $cookies in AngularJs. Is there a way to read a value of that cookie? If not, then can I implement CSRF in some other way?

I also tried to implement the creation of the csrf cookie on the Web UI itself in the browser but the browser does not send the cookie to the webservice as its in different domain.

So, my question is how to implement csrf protection for this kind of situation?

Passepartout answered 6/1, 2015 at 6:41 Comment(0)
L
24

You were on the right track with this:

I also tried to implement the creation of the csrf cookie on the Web UI itself in the browser but the browser does not send the cookie to the webservice as its in different domain.

The CSRF cookie isn't meant to be "sent" to the server, it is meant to be read by the client and then supplied in a custom HTTP request header. Forged GET requests (triggered by HTML tags such as <img src="">) from other domains cannot set custom headers, so this is how you assert that the request is coming from a javascript client on your domain.

Here is how you can implement the idea you were working on, imagine you have api.domain.com and ui.domain.com:

1) User loads the Angular client from ui.domain.com

2) User posts authentication information from Angular client to api.domain.com

2) Sever replies with an HttpOnly authentication cookie, called authCookie, and a custom header e.g. X-Auth-Cookie, where the value of this header is a unique value that is linked to the session that is identified by the authCookie

3) The Angular client reads the X-Auth-Cookie header value and stores that value in a XSRF-TOKEN cookie on its domain, ui.domain.com

  • So now you have:

    • XSRF-TOKEN cookie on ui.domain.com
    • authCookie cookie on api.domain.com

4) User makes a request of a protected resource on api.domain.com. The browser will automatically supply the authCookie value, and Angular will automatically send the X-XSRF-TOKEN header, and will send the value that it reads from the XSRF-TOKEN cookie

5) Your server asserts that the value of X-XSRF-TOKEN is linked to the same session that is identified by the value of the authCookie

I hope this helps! I've also written about token authentication for Angular, Token Based Authentication for Single Page Apps (SPAs) (Disclaimer: I work at at Stormpath)

Liv answered 16/10, 2015 at 16:30 Comment(2)
step 3, how can cookie sent by api.domain.com be read by ui.domain.com, they're on different domains.Vintager
@Vintager - in Step 3 the SPA on ui.domain.com is reading a custom header 'X-Auth-Cookie', not reading the cookie 'authCookie' itself which is not only a third party cookie but also 'httpOnly'Hayfork
B
3

Angularjs has built-in support for CSRF but unfortunately it doesn't work cross domain, so you have to build your own.

I managed to get it working by first returning a random token in the headers and cookies on the first request. In order to read the header you need to add it to Access-Control-Expose-Headers. This is then added to all posts

$http.get('url').
    success(function(data, status, headers) {
        $http.defaults.headers.post['X-XSRF-TOKEN'] = headers('XSRF-TOKEN');
    });

Then on the server you can compare the cookie value with the value in the header to ensure they are the same.

Blackmore answered 6/1, 2015 at 11:36 Comment(2)
I tried this and since server is from different domain, I dont think AngularJs was able to read that cookie. I tried manually and checked, there was no cookie in the object $cookies. How can I resolve this? Also I checked the link for AngularJS CSRF, it is mentioned there that the header will not be set for cross domain requestsPassepartout
I updated my answer. Please let me know if this does not solve your problem?Blackmore
D
0

$http docs : Angular provides a mechanism to counter XSRF. When performing XHR requests, but will not be set for cross-domain requests.

This is a small lib put together might help you https://github.com/pasupulaphani/angular-csrf-cross-domain

Deal answered 10/4, 2015 at 12:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.