Securely renewing a session without javascript and without breaking CSRF protection - security

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.

Related

Why does Express/Connect generate new CSRF token on each request?

As far as I understand there are two approaches in protecting from CSRF attacks: 1) token per session, and 2) token per request
1) In the first case CSRF token is being generated only once when the user's session is initialized. So there is only one valid token for the user at once.
2) In the second case new CSRF token is being generated on each request and after that an old one becomes invalid.
It makes harder to exploit the vunerability because even if attacker steals a token (via XSS) it expires when the user goes to the next page.
But on the other hand this approach makes webapp less usable. Here is a good quotation from security.stackexchange.com:
For example if they hit the 'back' button and submit the form with new values, the submission will fail, and likely greet them with some hostile error message. If they try to open a resource in a second tab, they'll find the session randomly breaks in one or both tabs
When analizing Node.js Express framework (which is based on Connect) I noticed that a new CSRF token is generated on each request,
but an old one doesn't become invalid.
My question is: what is the reason to provide new CSRF token on each request and not to make invalid an old one?
Why not just generate a single token per session?
Thank you and sorry for my English!
CSRF tokens are nonces. They are supposed to be used only once (or safely after a long time). They are used to identify and authorize requests. Let us consider the two approaches to prevent CSRF:
Single token fixed per session: The drawback with this is that the client can pass its token to others. This may not be due to sniffing or man-in-the-middle or some security lapse. This is betrayal on user's part. Multiple clients can use the same token. Sadly nothing can be done about it.
Dynamic token: token is updated every time any interaction happens between server and client or whenever timeout occurs. It prevents use of older tokens and simultaneous use from multiple clients.
The drawback of the dynamic token is that it restricts going back and continuing from there. In some cases it could be desirable, like if implementing shopping cart, reload is must to check if in stock. CSRF will prevent resending the sent form or repeat buy/sell.
A fine-grained control would be better. For the scenario you mention you can do without CSRF validation. Then don't use CSRF for that particular page. In other words handle the CSRF (or its exceptions) per route.
Update
I can only think of two reasons why single dynamic token is better than multiple:
Multiple tokens are indeed better but have at least one dynamic token like one above. This means designing a detailed workflow which may become complex. For example see here :
https://developers.google.com/accounts/docs/OAuth2
https://dev.twitter.com/docs/auth/implementing-sign-twitter
https://developers.facebook.com/docs/facebook-login/access-tokens/
These are tokens to access their API (form submission etc.) not just login. Each one implements them differently. Not worth doing unless have good use case. Your webpages will use it heavily. Not to mention form submission is not simple now.
Dynamic single token is the easiest, and the readily available in library. So can use it on the go.
Advantages of multiple tokens:
Can implement transactions. You can have ordering between requests.
Can fallback from timeout and authentication errors (you must handle them now).
Secure! More robust than single tokens. Can detect token misuse, blacklist user.
By the way if you want to use multiple tokens you have OAuth2 libraries now.

CSRF in Go Web Applications

I want to implement CSRF prevention in my Go web application. Users don't log in, but they do fill out forms and pay (via Stripe Checkout).
Posting something sets a key in a session variable (cookie) so they can later edit what they've posted, and a URL in an email allows them to come back when the cookie has expired and edit it again if need be.
From what I can see, I can use https://code.google.com/p/xsrftoken/ with the "double submitted cookie" method to implement CSRF prevention by:
Generate a CSRF token against an arbitrary user ID (uuid.V4() via go-uuid), like so:
if session.Values["id"] == "" {
session.Values["id"] = uuid.NewV4()
}
csrfToken := xsrftoken.Generate(csrfKey, session.Values["id"], "/listing/new/post")
... and store that in the session and render it in a hidden field in the template:
session.Values["csrfToken"] = csrfToken
...
<input type="hidden" id="_csrf" value={{ .csrfToken }}>
When the user submits the form, I need to get the ID I generated, confirm that the submitted csrfToken from the form matches the one in the session, and if so, validate it with the xsrf package to confirm it hasn't expired:
userID := session.Values["id"]
if session.Values["csrfToken"] != r.PostFormValue("csrfToken") {
http.Redirect(w, r, "/listing/new", 400)
}
if !xsrftoken.Valid(session.Values["csrfToken"], csrfKey, userID, "/listing/new/post") {
http.Redirect(w, r, "/listing/new", 400)
}
My pertinent questions are:
Should I generate a new token every time the form is rendered? Or is it acceptable to re-use a non-expired token for a single user session? Update: According to this answer I should only generate a new token per session (i.e. so the same user gets the same token on the same form, until the token expires)
Given the updated question, how do I handle the situation where a created token expires between the time the user requests the form and then submits the form? (perhaps it had 10 minutes left, and they alt+tabbed out for a while) Re-direct them back to the form (re-populated, of course!) and generate a new session id + csrf token?
Is there a different way to do this? Coding Horror indicates that SO generates a unique key for every HTML form sent to the client? How would I go about going down this route with the xsrf package, given that it wants a userID when generating a new key?
What else have I overlooked?
Should I generate a new token every time the form is rendered? Or is
it acceptable to re-use a non-expired token for a single user session?
Update: According to this answer I should only generate a new token
per session (i.e. so the same user gets the same token on the same
form, until the token expires)
It is a good idea to regenerate both the token and session ID often. Given a persistent attacker and a viable entry vector, it's just a matter of time until the attacker obtains both. If, however, at least one of both identifiers regenerates before the attacker is able to crack the current one, then no problem.
Given the updated question, how do I handle the situation where a
created token expires between the time the user requests the form and
then submits the form? (perhaps it had 10 minutes left, and they
alt+tabbed out for a while) Re-direct them back to the form
(re-populated, of course!) and generate a new session id + csrf token?
You can update cookies and CSRF tokens through AJAX if you want to give your client vast time to fill out a form.
Is there a different way to do this? Coding Horror indicates that SO
generates a unique key for every HTML form sent to the client? How
would I go about going down this route with the xsrf package, given
that it wants a userID when generating a new key?
The more tightly bound a token is to a certain action that requires authentication, the more fine-grained control you have. If you can uniquely identify each form in your application then I'd say do it.
I've created a CSRF protection package for Go called nosurf. Here's how it handles the areas you mentioned:
Token is created by taking bytes from CS PRNG and encoding them using base64. It is not regenerated for every page load or every form, though there is a user-callable function for regenerating the token.
It is then stored in a cookie (not a session, as it's a generic middleware not intended for any specific framework only). The cookie lasts one year, but you can easily modify this duration.
nosurf takes care of cancelling the request and either returning 403 or calling your custom failure handler (if set). You don't have to have if CsrfCheckOk(r) { ... } or anything like that in your code.
Sadly, it doesn't address token expiring inbetween the page load and the form submission.
So that's it, even though I'm not sure it is the best way to handle CSRF all-around. A package for a specific framework might handle it better in some ways due to tight integration.

How to set up XSRF protection in web apps?

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.

CSRF protection: do we have to generate a token for every form?

Do we have to generate a token, for every form in a website? I mean, every-time to generate different token for every requested form? If not, why?
In general, it suffices to have just one token per session, a so called per-session token:
In general, developers need only generate this token once for the current session. After initial generation of this token, the value is stored in the session and is utilized for each subsequent request until the session expires.
If you want to further enhance the security, you can use one token per each form/URL (per-form token) to mitigate the impact when one token leaks (e. g. XSS) as an attacker would only be able to successfully attack that specific form/URL.
But using per-request tokens, i. e. tokens that change with each request, rather cuts the usability of the website as it restricts parallel browsing:
To further enhance the security of this proposed design, consider randomizing the CSRF token […] for each request. Implementing this approach results in the generation of per-request tokens as opposed to per-session tokens. Note, however, that this may result in usability concerns. For example, the "Back" button browser capability is often hindered as the previous page may contain a token that is no longer valid. Interaction with this previous page will result in a CSRF false positive security event at the server.
So I recommend you to use either per-session tokens or per-form tokens.
No, you just need to generate a token on a per-session basis.
Tokens are very unlikely to be leaked accidentally by users and generating a token per form makes things very complicated if a user is browsing the site in two different tabs/windows at once.

CSRF protection by storing nonce in Session variable and form

To protect against CSRF you should put a nonce in a hidden field in the form, and in a cookie or in the session variable. But what if the user opens several pages in different tabs? In this case each tab would have a form with a unique nonce, but there would be only one nonce stored in the session variable or cookie. Or if you try to store all the nonces in the cookie/session variable, how would you identify which one belongs to which form?
You can store the same nonce in each of the forms. The easiest way to do it is to tie the nonce to the session ID, so that those forms only work in that session.
You will want to make it hard for attackers to snarf session IDs and create their own nonces. So, one way to go about it is to use HMAC-SHA256 (or the like) to hash the session ID, using a key that you do not expose to the public.
(Obviously if the attacker can get the actual session ID itself, they can already do session hijacking. So that's not what I'm talking about, but rather the ability for an attacker to craft a script (that runs on the victim's computer) that can somehow grab the session ID and use that to dynamically generate a URL with the nonce pre-filled.)
ETA: Whether the above approach is enough on its own depends on how long you expect your typical sessions to last. If users usually use long-lasting sessions spanning longer than a few hours, you'll need to use something more sophisticated.
One approach is to create a new nonce for each form, that contains the timestamp, as well as hash(timestamp . sessionid) (where hash is some variant of HMAC as described above, to prevent forgery, and . is string concatenation). You then verify the nonce by:
checking the timestamp to ensure that the nonce is fresh enough (this is up to your policy, but a few hours is typical)
then, calculating the hash based on the timestamp and session ID, and comparing against the nonce, to verify that the nonce is authentic
If the nonce check fails, you'll want to display a new form, pre-populated with the user's submission (so that if they took a whole day to write their post, they won't lose all their hard work), as well as a fresh nonce. Then the user can resubmit straight away successfully.
Some people do generate a token for each form, and that is a very secure approach. However, this can break your app and piss off users. To prevent all XSRF against your site you just need unique 1 token variable per session and then the attacker will not be able to forge any request unless he can find this 1 token. The minor issue with this approach is that the attacker could brute force this token as long as the victim is visiting a website the attacker controls. HOWEVER if the token is pretty large like 32 bytes or so, then it would take many years to brute force, and the http session should expire long before then.
What you're describing is not a nonce anymore (nonce = number used once), it's just a session identifier. The whole point of a nonce is that it is only valid for a single form submission, therefore offers greater security against hijacking than just a session ID, but at the cost of not being able to have multiple tabs operating in parallel on the site.
Nonces are overkill for many purposes. If you use them, you should only set and require them on forms that make critical changes to the system, and educate users that they cannot expect to use more than one such form in parallel. Pages which do not set a nonce should take care not to clear any previously stored nonce from the session, so that users can still use non-nonced pages in parallel with a nonced form.
Long time back this post was written.
I've implemented a csrf blocker that I'm almost certain protects well.
It does function with multiple open windows, but I'm still assessing the kind of protection it offers. It uses a DB approach, ie storing instead of session to a table.
NOTE: I use MD5 in this case as an easy anti-sqli mechanism
Pseudo Code:
FORM:
token = randomstring #to be used in form hidden input
db->insert into csrf (token, user_id) values (md5(token),md5(cookie(user_id))
-- the token is then kept in the db till it's accessed from the action script, below:
ACTION SCRIPT:
if md5(post(token)) belongs to md5(cookie(user_id))
#discard the token
db -> delete from csrf where token=md5(post(token)) and user_id=md5(cookie(user_id))
do the rest of the stuff

Resources