How to set up XSRF protection in web apps? - security

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.

Related

Securely renewing a session without javascript and without breaking CSRF protection

I am working on doing some security hardening on a legacy web application, and have run into a bit of a conflict.
So, I added CSRF protection to the application with a CSRF token as a hidden input in forms. Pretty normal.
Then I dramatically lowered the session timeout (the previous value was 8 hours, which obviously is unacceptable from a security point of view). However, to prevent users from losing their work when their session times out, I also implemented a modal login dialog with some JavaScript to renew their session before completing the form submission. This JavaScript also updates the CSRF token input with the new value from the server upon a successful login, as obviously the old CSRF token was associated with their previous now-expired session. Losing work is a huge deal for this application because users will genuinely spend a hour on a single page, just filling out data in a form and never hitting the save button, all the while the server doesn't know that the users are doing anything.
However, there is a not-insignificant portion of our userbase on machines with some pretty draconian policies forbidding JavaScript entirely. So, a secondary workaround is also needed for these people. If I were building the application from scratch, I feel the best solution for these users would be to simply re-populate all the inputs on the page they were on (prior to the timeout) with values from the POST data (after the user logs back in). However, implementing that would be such a gargantuan undertaking in this old code that it may as well be impossible.
We really had a hard time coming up with a workable solution for the non-JavaScript users. The best I've been able to come up with is to place a fixed link in the corner of the screen informing the user of when their session would expire, and redirecting them to the login page in a new tab if they click the link. That way, they can click the link and log in again before submitting the form. However, that breaks the CSRF protection, as upon returning to the form the CSRF token in the hidden input no longer matches the one in new session.
Short of refactoring literally thousands for forms, is there any way I can keep users without JavaScript from losing work when their session expires, without breaking CSRF protection?
Theory
If the CSRF token is not associated with a specific session, how does one prevent an attacker from obtaining their own CSRF token by visiting a page in the application, ...
One does not attempt to prevent an attacker from obtaining their own CSRF token. CSRF protection does not rely on attacker's inability to obtain and submit a valid CSRF token. CSRF protection depends on two CSRF tokens and relies on attacker's inability to obtain and submit back to the server two tokens that are related to each other. In practice, "related to each other" means "cryptographically bound to each other".
An attacker can obtain a valid CSRF token and maybe could even additionally obtain the second CSRF token which is also valid on its own. However the attacker won't be able to ensure both tokens are cryptographically bound to each other.
Implementation
There are many protection schemes. For example, looking at the high level like this:
The server creates two CSRF tokens and sends both to the client along with the session cookie. The first CSRF token is sent as a cookie (let's call it 'form cookie'), the second one is sent as the hidden form input you are currently using.
The server has a symmetric key that is never sent out, let's call it 'server key'. To create a form cookie, the server generates a random sequence of bytes (let's call it 'CSRF_key') and encrypts it using the server key. The encrypted output, serialised as a string, is sent to the client in the form cookie.
The server creates a string by concatenating the current timestamp and a GUID: timestamp+GUID. It then calculates a hash of that string using HMAC algorithm: HMAC(timestamp+GUID, CSRF_key). HMAC requires a key as the second argument and the server uses CSRF_key generated at the previous step as the HMAC key. The HMAC output is serialised as a string and concatenated with the timestamp+GUID string. The concatenation is sent out as the second CSRF cookie e.g. your hidden form input.
When the server gets POST with the form, it gets both CSRF tokens (one as a form cookie, another as an hidden input). It first checks the session, then it verifies that the timestamp (taken from the timestamp+GUID string) is valid e.g not too stale, not in the future etc. Then the server uses the server key to decrypt the form cookie and get the CSRF_key.
The next step: Calculate HMAC of the timestamp+GUID string using CSRF_key as the HMAC key. Compare the output with HMAC value in the hidden field. If identical, then CSRF check is ok so accept the form and generate another CSRF_key to be used as the 'form cookie' for the next form.
Note: In many real life scenarious the implementation is an overkill and could be simplified. Also it's a high level blueprint, there are important low level implementation details like sufficient keys length, proper cookie attributes etc.

How to send the CSRF token to the client when using synchronizer token pattern?

In the synchronizer token pattern, the server generates a random token and this token has to be submitted by each form submission by the client. How can the client get to know the CSRF token that was created in the server?
Upon GET requests, you can put the token into the resulting page. It can either be written in forms as a hidden field to have it sent automatically when a form is sent, or in something like a meta header (<meta name="csrftoken" content="...">), or pretty much any other way.
If it's just plain old form posts, a hidden field is probably the easiest. If javascript is involved (ajax requests), you will probably be better off with one central location in the page (like a meta header) where any javascript can take it from.
Also with forms, you can generate a different token for each new page download, but for ajax-heavy applications changing the token will lead to problems - for most applications, one token per login session is acceptable.

Is it necessary to generate anti-XSRF/CSRF token in server side?

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.

Difference between CSRF and X-CSRF-Token

What is the difference between use X-CSRF-Token in an HTTP header or token
in the hidden field?
When to use the hidden field and when to use the header and why?
I think that X-CSRF-Token is when I'm using JavaScript / AJAX but I'm not sure.
CSRF protection comes in a number of methods.
The traditional way (the "Synchronizer token" pattern) usually involves setting a unique valid Token value for each request and then verifying that unique value when the request is subsequently sent in. It is usually done by setting a hidden form field. The token value is usually short lived and associated to that session, so if a hacker tries to reuse a value they saw previously on the page, or tries to guess the value they will likely fail. So only requests from your application will work and forged requests from outside your application/domain (aka cross site request forgery) will fail.
The downside of that is it requires your application to set this hidden token on all HTML forms. These pages now have to be dynamically generated by an application, when perhaps previously they were static HTML. It can also break the back button (as you need to refresh the form to regenerate another unique CSRF value). You also now need to keep track of valid tokens on the server side and check any requests use a valid token. This can take quite a bit of extra effort to implement and maintain going forward.
An alternative approach (called the "Cookie-to-header token" pattern) is to set a Cookie once per session and the have JavaScript read that cookie and set a custom HTTP header (often called X-CSRF-TOKEN or X-XSRF-TOKEN or just XSRF-TOKEN) with that value. Any requests will send both the header (set by Javascript) and the cookie (set by the browser as a standard HTTP header) and then the server can check that value in the X-CSRF-TOKEN header matches the value in the cookie header. The idea being that only JavaScript run on the same domain would have access to the cookie, so JavaScript from another domain couldn't set this header to the right value (assuming the page is not vulnerable to XSS that would give access to this cookie). Even fake links (e.g. in a phishing email) would not work either, as even though they would appear to come from the right domain, only the cookie will be set but not X-CSRF-TOKEN header.
This can be MUCH easier to implement than the Synchronizer token pattern as you don't need to set the token for each call to each form, and the check is relatively simple too (just check the cookie matches the header) rather than tracking CSRF tokens validity. All you need is to set a cookie to a random value for each session. Some front end frameworks will even automatically generate the header for you if they see the cookie (e.g. AngularJS does this for example).
The downside is that it requires JavaScript to work (but that may not be an issue if your app basically doesn't work without JavaScript anyway) and also it will only work for requests the JavaScript makes (e.g. XHR requests) - regular HTML form requests would not set the header. A variation on this (the "Double Submit Cookie" pattern) puts the X-CSRF-TOKEN value in a hidden form field rather than in an HTTP Header to get around this but still keep the server side logic simpler than the traditional Synchronizer token pattern. It should be noted however that OWASP states some weaknesses with the Double Submit method, when the attacker is able to set the cookie (which is often easier than reading the cookie) so recommends validating the CSRF token in this case.
Additionally the Synchronizer token pattern can allow extra controls to enforce flow (e.g. the hidden field CSRF token will only be set when the application thinks you have sent a valid request in to get that form).
Oh and some security scans will pick up the fact the cookie is not set with the HTTP-Only flag so can be read by JavaScript - but that's deliberate as it needs to be able to read by that! False alert. You'd think as long as you are using a common name like X-CSRF-TOKEN they would know not to flag this, but have seen it flagged often.
All of them are for cross site request forgery protection and you need to use just one of them when sending a request to backend. Different names comes from different frameworks.
It's all about sending a csrf value to backend. Then backend will compare it with the csrf value stored in database for that specific user.
csrf:
Is used in HTML forms (not AJAX)
Produced in backend while rendering HTML form.
we can not set request header in HTML forms directly, so we have to send it via form input as a hidden field.
you can name this hidden input whatever you want.
E.g.: <input name="my_csrf_input" value="a_hashed_string(the csrf value)"
X-CSRF-TOKEN:
It is added to the request HTTP header for AJAX requests.
To use it, we can put the csrf value in a <meta> tag while rendering the HTML, then in front end we can get the value from that <meta> tag and send it to backend.
Laravel specific:
When using Laravel as backend. Laravel checks this header automatically and compares it to the valid csrf value in database (Laravel has a middleware for this).
X-XSRF-TOKEN:
It is added to the request header for AJAX requests.
Popular libraries like Angular and Axios, automatically get value of this header from XSRF-TOKEN cookie and put it in every request header.
To use it, we should create a cookie named XSRF-TOKEN in backend, then our front end framework that uses Angular or Axios will use it automatically.
Laravel specific:
Because it's popular, Laravel creates this cookie in each response.
so when you're using for example Axios and Laravel you don't need to do anything, just log user in and 'auth' middleware will do the job.
It's a bigger string compared to X-CSRF-Token because cookies are encrypted in Laravel.

CSRF: Can I use a cookie?

Is it ok to put the CSRF token in a cookie? (and in every form, as a hidden input, so I can check if they match, of course) I heard someone say that doing so, beats the whole purpose of the token, though I don't understand why. It seems secure to me.
And if it is secure, is it any less secure than puting the token in the URL's ?
Is there any other method?
Where can I read more on the subject?
UPDATE: So far no one can tell me how is the cookie method insecure, if it still has to match the token from the form, which the attacker shouldn't be able to get, unless he uses another hack like XSS, which is a different matter, and still doesn't make a difference between using cookie and url token.
UPDATE 2: Okay, seems like some famous frameworks use this method, so it should be fine. Thanks
Using cookies works, and is a common practice (e. g. Django uses it). The attacker cannot read or change the value of the cookie due to the same-origin policy, and thus cannot guess the right GET/POST parameter.
Check out the Encrypted Token Pattern, which allows stateless CSRF protection without the need to store tokens on the server.
If you decide to put the CSRF-token in a cookie then remember to mark that cookie as HttpOnly. If your site has a cross-site scripting vulnerability the hacker won't be able to read the CSRF-token. You can check the cookies that can be read by JavaScript using the command console.log(document.cookie) in any modern browser console. If you have session cookies or other sensitive cookies these should also be marked as HttpOnly.
Further reading:
https://www.owasp.org/index.php/HttpOnly
"CSRF works because many sites use GET requests to execute commands.", so, many sites don't use the GET method as expected, because these request must be idempotent: see the rfc2616.
"The CSRF parameter is already there in the cookie and it gets sent along with the session.", so how?
The cookie is only used has a token storage, as the DOM when we set the token in a hidden input field. A piece of javascript must get the token value from this cookie, and set it as a parameter in the URL, the request body or in the request header. It will be check on the server with the value stored in the session. That's the Django way to handle the CSRF token.
Javascript can't access the cookie from another domain, due to the cross domain browser protection, so I don't know how a malicious user can force someone to send the correct token along a forged request. With an XSS, yes, but XSS defeat the common CSRF countermeasures.
I prefer giving this clarification, because I think it's an important question and not so easy to handle.
GET request must be used to get a resource and/or display its data, it must not be used to change its state (deletion, property incrementation or any changes).
The CSRF validation must be done server-side, it seems to be obvious, but I put it as a reminder. This method can't be a vector of attack if you observe this recommandations.
Using a cookie defeats the purpose of CSRF. Here's why:
CSRF works because many sites use GET requests to execute commands. So say Bob has some kind of administrative web account and he's logged into it. Some request could be made like:
http://somesite.com/admin/whatever.php?request=delete_record&id=4
So now Bob gets linked to an attack site (someone is trying to mess with his data). The attacker then loads the above URL in an image, probably with another ID and deletes some other record. The browser loads it because Bob is already logged into his admin site so he has a valid session.
CSRF seeks to eliminate this by adding a secure parameter to the transaction. That parameter should rotate on every request and then be resent by the browser. Making the URL look something like this:
http://somesite.com/admin/whatever.php?request=delete_record&id=4&csrf=<some long checksum>
The idea is that now the attacker has to guess "some long checksum" to create an attack. And if that checksum rotates on every request well it should be virtually impossible.
BUT if you store that checksum in a cookie you're back at square 1. The attacker no longer has to guess it. He just crafts the original URL. The CSRF parameter is already there in the cookie and it gets sent along with the session. It doesn't stop the insecure behavior from happening.

Resources