Basic cookie & CSRF question - security

I'm currently learning more about CSRF and I have a basic question about cookies. From Jeff Atwood's article on CSRF:
"When a POST request is sent to the
site, the request should only be
considered valid if the form value and
the cookie value are the same. When an
attacker submits a form on behalf of a
user, he can only modify the values of
the form. An attacker cannot read any
data sent from the server or modify
cookie values, per the same-origin
policy. This means that while an
attacker can send any value he wants
with the form, he will be unable to
modify or read the value stored in the
cookie."
If cookies are a piece of text stored on a users computer, how can they not modify/read the value of a cookie?
If they knew the value of the cookie and can see a pseudorandom value hidden in a form, wouldnt they have all they need to perform an attack?
Thanks,

Same origin policy means that an attacking website is unable to read the cookies from another domain. See http://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_cookies
We're not talking about an attacker who has a rootkit or something of the sort on a user's computer, what CSRF protects from is a malicious server having a user submit a form via a POST request to a domain that the malicious server wants to attack. Because the malicious server can't read the cookie from the target domain, they can't properly fill out the CSRF field for the POST request they're making.

What this is referring to is the synchroniser token pattern. What it usually means is that a form contains a hidden field with a value that is unique to that user's session. The same value is stored in a cookie in the user's machine. When the form is submitted, both values are checked for a match.
The advantage of this approach is that if a malicious website attempts to construct a post request to the legitimate website it won't know that hidden form value. It's an altogether more complex process to obtain this.
The attacking site can't read or manipulate the cookie value because it was issued from another domain. More on this (including a worked example) here: OWASP Top 10 for .NET developers part 5: Cross-Site Request Forgery (CSRF)

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.

Synchronizer token pattern: How does it prevent combination of XSS and CSRF?

I have been looking into the OWASP recommendation to prevent CSRF attacks (https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet).
Now, what I do not understand is how this would prevent an attack that is a combination of an XSS and a CSRF attack. Let's say we have the following attack scenario:
Attacker is able to perform a stored XSS attack, so that the script that the attacker inserts on the website is executed everytime a user visits the page.
This script will completely redesign the DOM, for example instead of the original form where a user needs to give some irrelevant information, the attacker's script redesigns it so that this form will be redesigned to a form where a user with admin privileges is added. Note that the user will not see this, as the labels of the fields will remain the same. Only the POST will be different.
The attacker knows that this website uses anti-CSRF tokens. Looking at the OWASP recommendation: '(..)application should include a hidden input parameter with a common name such as "CSRFToken"', the attacker knows that most websites will have a hidden field with this id somewhere on the page.
the attacker makes sure the value of this field is also submitted in the fake POST. Even though the attacker doesn't know the value of this hidden field, it can specify in the POST that this value should be sent with the request. This is possible, as the user's DOM has been modified, the request will come from the user's browser, the user's cookies will also be sent with the request.
The user submits the form, and the fake user is created.
It seems to me this cannot be prevented by just using a CSRF token. Or is an implicit assumption of the synchronizer pattern that XSS attacks have been neutralized?
Or is an implicit assumption of the synchronizer pattern that XSS attacks have been neutralized?
Yes. If your website is attacked in this way then it is an XSS attack rather than CSRF. CSRF simply means the request is made "cross site", whereas in your example the request is on the same site - it is just the scripting that is "cross site".

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.

Tricky question for good understanding of CSRF

My friend and I have a pari for beer.
From wikipedia:
Requiring a secret, user-specific
token in all form submissions and
side-effect URLs prevents CSRF; the
attacker's site cannot put the right
token in its submissions
The atacker can use browser cookies indirectly, but he can't use them directly!
That's why he can't put the cookies into the link using document.write()
Let us look how the logout link is generated. Is it secure way? Can this GET request be faked?
function logout(){
echo '<a href="?action=logout&sid='.htmlspecialchars($_COOKIE['sid']).'>Logout</a>';
}
sid is a session ID, generated for every session
on the server side, the following checking is performed:
$_GET['sid']==$_COOKIE['sid']
Absolutely not! Never use session identifiers for CSRF protection.
As far as why? Well, the answer is simple. Doing so opens the door for session hijacking attacks. Imagine someone copies and pastes the link for some reason into an email or onto the web. Now, the person on the other end of the email has the session identifier of that session. Sure, if they click the link it won't activate the session, but someone who knows what they are doing will still be able to use it.
And don't use a secret cookie either. Cookies are transmitted on every request. So the mere existence of a cookie does not verify that the user intended to make the request.
How to do it instead? Follow the OWASP recommendations. Use a unique, random token that's issued on each request and is associated with the session. Then verify that the token is valid on submission and then invalidate the token! It should be a one-time-use token only. Have it submitted by the form, and not attached to a link directly...
This prosed security system is immune to CSRF. The reason why this works is because in a CSRF attack the browser keeps track of the cookie, so the attacker doesn't need to know the cookie value when he is building the request. If this proposed security system where vulnerable to CSRF an exploit like the following Proof of Concept would log out a browser:
<img src=http://victim_site/index.php?action=logout&sid= />
Clearly in this case sid needs a value, and an attacker cannot obtain this value without using XSS, which makes this a moot point. Using xss an attacker can read a CSRF token to forge requests. This was used in the MySpace worm Sammy.
The use of the cookie a valid, however weaker form of CSRF protection. One problem is that it totally undermines http_only cookies. Ideally a CSRF token and a session id should be a Cryptographic nonce. However, it is more secure to have them be separate values.
Edit: This answer is at least partially wrong. Using the session ID as a CSRF token could lead to session hijacking if, eg, links are copy+pasted around. See ircmaxell's answer and comments.
Yes, because the session ID is both random and associated with the user, it would be an acceptable form of CSRF protection.
That said, it would be even safer to use a different random number, on the off chance that malicious JavaScript is able to grab the session cookie (and session ID)… But if I had to choose between “no CSRF token” and “session ID as a CSRF token”, I'd always pick the session as a CSRF token.
The only potential problem with using session IDs as CSRF tokens is: if someone was able to steal a CSRF token, they would also be able to hijack the associated session… But I can't think of a sensible scenario where that would be an issue.
Now, from the discussion on Marc B's answer, below: using a nonce would provide other benefits (like preventing duplicate form submissions)… But it isn't any more secure against CSRF attacks than the session ID (with the one caveat I mention in the first second paragraph).
See also: CSRF Validation Token: session id safe?
And what's to stop someone from editing the HTML that you send them, as well as the cookie, which you've also send them? Both are are under the control of the user.
With firebug I can trivially change the contents of any page, as well as any cookie.
Now, if you'd modified your version so that the SERVER stores that ID, then it would be harder to hack...
$_SESSION['form_token'] = 's33krit valu3';
if ($_POST['form_token'] == $_SESSION['form_token']) {
... everything's ok ...
}
Since the session data is kept on the server, out of the attacker's hands, this is far more secure than trusting the attacker won't think to modify the cookie.
You owe your friend a beer.

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