Using React on the frontend with a RESTful API as backend and authorisation by a JSON Web Token (JWT), how do we handle sessions? For example after login, I get a JWT token from REST. If I save it to localStorage I am vulnerable to XSS, if I save it to cookies, same problems except I set cookies to HttpOnly, but React can't read HttpOnly Cookies (I need to read cookie to take JWT from it, and use this JWT with REST requests), also I didn't mention the Cross Site Request Forgery (CSRF) problem. If you're using REST as backend, you can't use CSRF Tokens.
As a result, React with REST seems like a bad solution and I need to rethink my architecture. Is it possible to offer your users a secure React application that handles all business logic on the REST API side without fear of losing their data?
Update:
As far as I understood, it is possible to do this:
React makes an AJAX call to the REST API
React gets a JWT token from the REST API
React writes HttpOnly cookie
Because React can't read HttpOnly cookies, we use it as-is in all our REST calls where we need authentication
The REST API calls to check the XMLHttpRequest header, which is some kind of CSRF protection
The REST API side checks for cookie, reads JWT from it and does stuff
I lack theoretical knowledge here. The logic looks pretty secure, but I still need an answer to my questions and approve of this "workflow".
React makes AJAX call to REST API
assured, lots of restful resource client lib available
React gets JWT token from REST
assured, this is what JWT should do
React writes httponly cookie
I don't think so, It should not work, but session is not such a important thing, it'll soon get out of date, and recheck password on key operations, even the hackers got it in a very short time, you can bind session token together with IP when user login and check it in your backend apis. If you want it most secured, just keep token in memory, and redo login when open new page or page refreshes
Because react can't read httponly cookie, we use it as-is in our all REST call where we need authentication
assured, check user and permissions through login token, like csrf you can put your login token into your request header, and check it in your backend apis.
Bind login token to your own restful lib will save you a lot codes
REST on calls checks XMLHttpRequest header, what is some kind of CSRF protection
REST side check for cookie, read JWT from it and do stuff
assured, as most people do.
Also, bind csrf token to your own restful lib will save you a lot codes
use user token in header https://www.npmjs.com/package/express-jwt-token
Authorization JWT < jwt token >
use csrf token in header https://github.com/expressjs/csurf
req.headers['csrf-token'] - the CSRF-Token HTTP request header.
restful client https://github.com/cujojs/rest
react with jwt https://github.com/joshgeller/react-redux-jwt-auth-example
Your server can set the JWT cookie directly as a response to the login request.
The server responds to POST /login with Set-Cookie: JWT=xxxxxx. That cookie is http only and therefore not vulnerable to XSS, and will be automatically included on all fetch requests from the client (as long as you use withCredentials: true).
CSRF is mitigated as you mentioned, see OWASP for details.
Related
so i am doing an OAuth between my FrontEnd (ReactJS) and my BackEnd (Spring Boot),
since i am setting a cookie httpOnly, my frontend cant access it obv.
but now, since the token needs to be send on every request to check if the user is Authorized or not, how we go about that?
If i send any request with axios for example, how do i get the Authorized Token in the header?
Well, you can't. If the cookie is httponly, there is no way to add its content to the Authorization header. You either need to store tokens directly in the JS code (e.g. in local storage or memory - taking into consideration the risk), or you need to add a proxy between the APIs and your SPA. The proxy will extract the token from the cookie and place it in the Authorization header.
I've been provided with a REST API which has authentication type bearer (Security Scheme Type: API Key, Header parameter name: Authorization) and which i use to authenticate the user and then fetch other data (i will only create the front end using react).
As a first step the user logs in and i sent his/her username-password to the prementioned REST API and get back an access and a refresh token.
Is anything wrong with storing these 2 tokens in a cookie in order to use them in subsequent requests? How does JWT comes into play regarding these 2 tokens? Is JWT of any use to me in this situation?
There's nothing wrong in storing the tokens in cookies, but if you're planning to have a Single Page Application with React it should be enough to store these tokens in memory. Once the user refreshes the page you can either make them sign in again or perform a silent login in the background to get a new set of tokens. In both cases the session kept on the Authorization Server should kick in and you should get new tokens without the need of user interaction.
Your tokens will be much safer if you don't keep them in cookies.
JWTs are not a requirement for access and refresh tokens. If you don't have to use them I would recommend going with opaque tokens. That said, since you do not have control over the API you might be limited to the format required by the API. If you don't want to be limited by this format you can set up your own gateway which you can use to perform token exchange or introspection and forward requests to the API with proper tokens (something which is called a Phantom Token pattern.
From my understanding of the question, you are using an identity provider which provides you with access token and refresh token for the users. That means it is a authentication as a service REST API at works here.
The REST API requires an authorisation header to be passed along with the username-password to exchange for access token and refresh token for the users. (correct me if I'm wrong) In this case, you might want to keep the authorisation header away from the users (consult the authentication as a service documentation).
You call the REST API with payloads (the user-password) along with headers like this:
Authorization: ACCESS_TOKEN
However the ACCESS_TOKEN is the one provided by the vendor for you to use the REST API. On success call of the REST API, it should return you with a set of access token and refresh token. You can then use this access token and refresh token to safe guard your own API, API that you control to provide service to your users.
The access token and refresh token might just be JWT tokens (again consult the vendor documentation).
Also if you are using an authentication as a service REST API, check the documentation if they provide a client sdk. In that case, it should show you the best practise of handling the access token and refresh token it returned.
In order to prevent CSRF, Spring Security and AngularJS uses a method to store the CSRF token in cookie. Then, for each request, the web client sends the same token as a cookie and HTTP header to the server for validation. These come from this blog. https://spring.io/blog/2015/01/12/the-login-page-angular-js-and-spring-security-part-ii#csrf-protection
However, I think that from the other side, the hacker can fake the cookie and HTTP header to send a fake request. Is this correct?
What I understood from ASP .net side of CSRF token for validating anti forgery token is, there will be two places where this token is stored.
1. In the page, which when request is made from angular, we will include this token in the request header.
2. In cookie, which will be automatically picked up by the request when triggered from browser.
In the server side, when the request is received, server will decrypt both the tokens and validates the request if both have a meaningful relation(this relationship is decided in the server with an algorithm) between them.
Now, here, an attacker couldn't figure out those 2 strings at any cost of time because the anti forgery validation algorithm is not disclosed and every time new strings are generated on the go.And second thing is I think attacker cannot directly include the cookie into the request because cookie is picked up from the browser. Their might be tools to do that. But unless the algorithm which is used is not disclosed, attacker could not accomplish a csrf attack.
I think this might be same for the Java spring side.
Context: Angular site is hosted on S3 behind CloudFront, separate from Express server that is used as API and almost all requests are XMLHttpRequests. All requests are sent without cookies (withCredentials = false by default) and I use JWT Bearer token for authentication by taking it from cookies in angular and placing to Authorization header (This technique is kind of what is described in CSRF Wiki page).
On Express site I do not allow Cookie header in Access-Control-Allow-Headers.
Cookies have secure: true flag, and are NOT httpOnly because I need to manually access them in angular.
Also I've read in this Medium article that JSON-Web-Tokens(JWT )/Bearer Tokens
is without a doubt one of the best methods of preventing CSRF
Question 1: Will I add extra security if I'll add X-XSRF-Token header to each request and for example make the mechanism stateless by checking for that same value in JWT payload? (I'we read about it in this thread)
Question 2: Do I actually need extra security efforts agains CSRF taking all that I described?
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?
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)
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.
$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