I'm trying to figure out how to implement authentication/authorization with React and Node, using httpOnly cookies, with scalability optionality. I've read JWT can solve this by using refresh tokens and httpOnly cookies.
The number one problem for me is how to make it work the right way with react, because the httOnly cookie isn't accessible via JS.
The second thing is, I want my frontend and backend to live in separated, dockerized containers, but as far as I got it, the httpOnly cookies are added to the headers only for the same domain(and same port).*Update Actually, I've just read this is not true and I can use httpOnly cookie on same host, but different ports.*Update2, I've remembered what it was about: the "SameSite" property on the cookie. This will bind the cookie to the same origin, meaning port difference will consider as different origin and the cookie will not be transferred.
Can you please clarify this and maybe explain, does it really so important, I mean am I really will be in danger if I want to use some super-ultra-highly-secure website and yet, will use JWT with local storage(but with refresh tokens)? Does the httpOnly(and sameSite: true) cookies really will make a difference here?
Thanks
In my project (which is also Node + React) I did the following:
For each authenticated user (username + password) I signed two tokens, an access token and a refresh token.
I set the refresh token in the response header using httpOnly attribute. beware that latest versions of browsers won't allow third party cookies (from different domains) unless you set the attributes Secure and SameSite.
I return the access token as my response's body
Now in the client side (your React app):
You will get the access token in the response body, and here you have two options. To store it in local storage, or in memory. I chose to keep the token in memory instead because of a XSS vulnerability. (This obviously requires you to get another access token on each page refresh since the memory is cleared)
The refresh token is not accessible through JS you are right, but you don't need it. This token will be sent on each request, and usually it's expiration date should be a long one (I set it to 6-12 months)
Every time you want to gain a new access token (because it's expiration time should be
a small one, about 15-20 minutes), you will send a request to the server to "refresh" your access token, and the server will just check if the refresh token exists in the request headers, if not return a status of Unauthorized
EDIT: jwt tokens are not "secure" or "not secure", they are just tokens containing some data, and they allow you to make your authentication processs stateless (instead of managing sessions in your server). As long as you don't store any sensitive data in them, and sign them with a secret key that only you possess, you should be fine
EDIT 2: Since you are using react which "sanitizes" the DOM, a possibility of XSS is a minor one, so using local storage is ok and lots of websites do it. But because there is still a possibility (of you or a client injecting a script to some input element), I decided to store the access token in memory, which is a bit of a overhead but I prefer that.
Related
I have an API in Next.js (NextAuth.js) that only the frontend will be using. It uses cookies for authentication. My question is could a malicious website change the user's data using CSRF? Should I implement CSRF tokens or can I prevent malicious websites from changing the data without it?
If authentication is based on something that the browser sends automatically with requests (like cookies), then yes, you most likely need protection against CSRF.
You can try it yourself: set up a server on one origin (eg. localhost:3000), and an attacker page on another (eg. localhost:8080, it's the same as a different domain, controlled by an attacker). Now log in to your app on :3000, and on your attacker origin make a page that will post to :3000 something that changes data. You will see that while :8080 will not receive the response (because of the same origin policy), :3000 will indeed receive and process the request. It will also receive cookies set for :3000, regardless of where the user is making the request from.
For mitigation, you can implement the synchronizer token pattern (csrf tokens), double submit, or you can decide to rely on the SameSite property of cookies, which are not supported by old browsers, but are supported by fairly recent ones, so there is some risk, depending on who your users are.
I have a question about Sessions and Cookies on Node regarding where they are stored and how they work.
To begin with, I understand the following to be true:
With a cookie, it is possible to specify how long it will store your data;
A session saves data while the browser is open;
Cookies are on the client side;
Session is on server side;
Then the following questions arise:
How does the browser and/or the server know that the user has already
logged in and does not need to log in again?
If the Session stays inside a cookie what's the difference?
Where are cookies stored? In the web browser?
I use the (Blackberry?) passport (browser?) but it does everything by itself. I want to better understand how it works behind the scenes.
My affirmations can be wrong. You can correct me, but please explain to me.
Regarding what you understand to be true:
Yes, when setting a cookie, you can specify how long it will persist. In the article HTTP Cookies in Node.js, see the section entitled
"Adding Cookie with expiration Time".
Yes, data can be stored in a
session if it is explicitly placed there by application code. Your server software may also use it to store other information. Here
is a nice short YouTube video on node.js sessions.
Cookies are stored in a file on your computer which is managed by your web
browser, so again, correct. Here's a nice article that explains in more detail: Cookies - Information that websites store on your computer.
As to your other questions:
How does the browser and/or the server know that the user has already
logged in and does not need to log in again?
It generally knows this by storing a cookie in your browser whose value is some sort of session ID that acts as an authentication token. When you are successfully authenticated, it will store a cookie and send this cookie's value as an HTTP header or as part of the URL string, etc. each time you make a request to the server. This token is stored on the server with some sort of expiration time, usually something like 15-60 minutes. The expiration timer is reset to zero with each successful request. If session timeout is 30 minutes for example, the token will be invalid after no request is made within 30 minutes. Therefore, if you walk away from your computer for an hour and try to access another page, you will likely be told you need to log in again.
If the Session stays inside a cookie what's the difference?
As I stated in the answer to the previous question, an authentication token is generally stored as a cookie and sent with each request. It's good to use over and over until the session times out.
So, the difference is: A session is stored on the server. A cookie is stored as a file on your computer by your browser. A session cookie is stored on your computer which is used by the server to track individual user sessions.
Where are cookies stored? In the web browser?
Yes, as stated above, cookies are stored in a file on your computer which is managed by your web browser. See the article I linked to above for more detail.
First off, some general facts.
A cookie is stored in the browser and then sent back to the target server with every request to that server.
A cookie can either contain actual state data (such as backgroundColor=blue) or it can just contain a token that only means something to the server.
Whoever sets a cookie decides how long they want it to last before it "expires". If the server sets the cookie (as cookies can also be set from within Javascript in the web page), then the server decides how long they want the cookie to last.
A server session consists of the server creating a unique token and putting that in a cookie that it sets for that browser. In parallel, it also creates a session object that is stored on the server and it creates a means of associating the token with a particular session object such that when a request comes in and it has a particular token in it, the server can find the corresponding session object.
Note, sessions don't have to use cookies. They can also put a session id in the URL itself and that is occasionally used, but isn't very popular for a variety of reasons.
How does browse and / or server know that the user has already logged in and does not need to log in again?
A server can consider a browser to be already logged in if it finds an appropriate cookie in the incoming request and if it finds an associated session object in the server-side session store and if that session object is both logged in and not expired.
If the Session stays inside the cookie why is this difference?
Usually, when using server-side sessions, all that's in the cookie is a unique token - not any of the actual session data.
Where is the cookie stored? In our browser?
Yes, it's stored on your hard drive by the browser and then sent as an http header along with every request to the server that the cookie is associated with.
Almost all doc about anti-CSRF mechanism states that CSRF token should be generated in server side. However, I'm wondering whether it is necessary.
I want to implement anti-CSRF in these steps:
There is no server-side-generated CSRF token;
In browser side, on every AJAX or form submission, our JavaScript generates a random string as token. This token is written into cookie csrf before actual AJAX or form submission happens; and the token is added to parameter as _csrf.
In server side, each request is supposed to have cookie csrf and submitted argument _csrf. These two values are compared. If they are different, it means it is a CSRF attack.
The server side doesn't need to issue CSRF token, just do the checking; and the token is totally generated in browser side. Of course, this is only for anti-CSRF. There should be authentication process in server side to validate user id.
It sounds a valid solution for CSRF, but I'm not sure why there is no documentation about this approach.
Is there any fault in this anti-CSRF mechanism?
As far as I understood, what you want to do is to create your anti-CSRF on the client side, store it in a cookie and also add it as a request parameter, so when the server reads your request, just verifies that your CSRF token cookie and parameter matches, and it decides if it's a valid request or not.
The reason to generate the anti-forgery token on the server side, is that the server will create that token and only the server will know the correct value, so if that parameter is slightly tampered on the client side, it will not be identical to the one stored in the server, and that will be enough to flag the request as a cross site request forgery attack.
Any client-side generated data can be tampered by an attacker and because of that, you can't rely on that information, for example, in your approach, you create a random value in your client side and you assign that value to your CSRF cookie and to your _csrf parameter, let's say that your value is "h246drvhd4t2cd98", but since you're only verifying that your 2 variables from the client side have the same value, an attacker can easily just create his CSRF cookie and variable with a value like "I'mByPassingThis" on both of them and your server will flag it as a valid request, so you're not getting security at all.
On the other hand, if the token is generated in the server, the attacker has no way to know the expected value, and that value will be different on every request, so the attacker's best approach will be just to try to guess it, which should be virtually impossible, unless you're using a predictable random number generator on the server side.
Also, if you want to create your own anti-forgery token mechanism, you need to take into consideration to use a cryptographically secure pseudo random number generator, but honestly, you should not bother with that, since the current server-generation process is just what you need (assuming that your framework has a built-in mechanism for this, if not, then you still need to make sure that you're using a cryptographically secure pseudo random number generator to generate your anti-forgery tokens).
Remember to never trust user's submitted information. Since it can always can be tampered, you always need to perform a double-check in the server side, and in this case, generating your anti-forgery token in the server is what allows you to double-check to verify the integrity of the submitted anti-forgery token.
I suggest to use this approach, I have used on a large scale project:
From: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#use-of-custom-request-headers
Use of Custom Request Headers
Adding CSRF tokens, a double submit cookie and value, an encrypted token, or other defense that involves changing the UI can frequently be complex or otherwise problematic. An alternate defense that is particularly well suited for AJAX or API endpoints is the use of a custom request header. This defense relies on the same-origin policy (SOP) restriction that only JavaScript can be used to add a custom header, and only within its origin. By default, browsers do not allow JavaScript to make cross origin requests with custom headers.
If this is the case for your system, you can simply verify the presence of this header and value on all your server side AJAX endpoints in order to protect against CSRF attacks. This approach has the double advantage of usually requiring no UI changes and not introducing any server side state, which is particularly attractive to REST services. You can always add your own custom header and value if that is preferred.
This technique obviously works for AJAX calls and you have to still need protect tags with approaches described in this document such as tokens. Also, CORS configuration should also be robust to make this solution work effectively (as custom headers for requests coming from other domains trigger a pre-flight CORS check).
So, instead of sending the token through a request body parameter, you could store and send to the server with a request header parameter.
I am a web application newbie. I know that XSRF protection questions have been asked in SO but the questions have been specific to a particular language (RoR/Python for example) or a library (jQuery). I would like to know how I can implement XSRF protection in my web application.
My understanding is that XSRF protection relies to using a unique random token that must be authenticated when a HTTP request is made. I have the following questions:
When should the authentication token be initialized? Should it be set on page load (i.e. GET request)?
Where should the token be initialized? Should they be set in input fields, cookies or request headers? How is this random value generated? How do I persist this value in order to be used for comparison?
When should the authentication token be verified? How does I compare authentication tokens? How do I compare these tokens with the token that I have persisted?
Is there a difference in the set up for a synchronous form request versus an AJAX request?
When should the authentication token be initialized?
The first time, during a session, that the user arrives at a page containing any form that you wish to protect from CSRF attacks.
Should it be set on page load (i.e. GET request)?
It should be embedded in the form when the HTML for it is generated.
Should they be set in input fields, cookies or request headers?
Hidden inputs.
Using a cookie would miss the point (which is that it comes from the page and does not persist in the browser). Extra headers can only work when using XHR, use a general approach.
How is this random value generated?
With a random number generator
How do I persist this value in order to be used for comparison?
Sessions
When should the authentication token be verified?
As part of the authorization step.
How does I compare authentication tokens? How do I compare these tokens with the token that I have persisted?
if ( $request->paramaters->{csrf} eq $session->data->{csrf} )
Is there a difference in the set up for a synchronous form request versus an AJAX request?
No. You still have a session and you still have a piece of the POST data being the token.
I'm not going to talk about a specific solution that you must follow as there are many, I'll talk about the main idea instead and you could implement it however you want.
The key idea to prevent XSRF is to store a random token in a place where code from other domains cannot access (such as a cookie). You could generate this token on server side and instruct the browser to store it locally for the entire session. (don't need to persist anything on server side)
Whenever you perform a request, send:
the token in your storage (if you use cookie, the browser will send it automatically).
the same token as the stored one in your request (whether as a header or in body using hidden field depending on how server code gets it)
On server side, the server will check for a match.
A sample code in jquery (with jquery cookie plugin):
$.ajax({
url:"someurl",
data:{token:$.cookie('token')}
});
The ability to read the cookie proves that this is from your domain, not external domains
There are various ways to implement this mechanism (don't need to stick to a specific solution) as long as they stick the the main idea:
Storing a secret token on browser in a place code from other domains cannot read it.
Send the secret token and the same token from browser to your server. The ability to send that same token proves that this is not a XSRF request.
How do I persist this value in order to be used for comparison?
We could store it as a cookie or as a session variable. Personally, I prefer cookie because:
Reduce memory consumption on server side.
We don't need to include that token in every generated HTML file in order for the browser to send it back.
Is there a difference in the set up for a synchronous form request
versus an AJAX request?
No, as long as you could prove this is not a XSRF request by sending the same token in the request. It does not matter where token is (a hidden field, a custom header,..). In case of Form, people usually send it as a hidden field.
Instead of using a cookie I'm using a JWT token which gets send with every request. Every request is a POST request so that the token does not get saved in the browser's history.
It's a single-page app.
The token looks like:
{
userId: 12345678,
expires: <UNIX timestamp>,
otherInfo: <something>
}
Everything is SSL secured.
The token is created on the server when the user logs on.
Would this be a good way to replace a cookie or do you see any flaws?
No, this is not a good solution. Using cookies (with a httpOnly flag) for cross-request persistence is not optional - it's the only way to safely store session credentials, in such a way that on-page JavaScript code cannot access it directly.
This is essential to prevent eg. session stealing in an XSS attack, by ensuring that scripts cannot access the credentials, but they can still be used in requests to the server.
Your use of JWT doesn't seem to really solve a problem, either - why can't you just use session cookies using an existing session implementation? This kind of thing is precisely is what they're made for.