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.
Related
I am testing a web application. CSRF is applied and sent in cookies and header but not in the form as hidden input. The csrf token does not change for every request but it change during the session. How often should the csrf token change ? Should it change per session or per request ? should the client or the server set the csrf token ? what is the best strategy to apply csrf protection? double submit cookie ? Triple Submit Cookie ? or any other new strategy ?
I'm just going to attempt to answer your questions one by one here.
How often should the CSRF token change?
You can change your CSRF token once per session. Changing it once per request offers no real security advantage and if anything, only serves as an easy way to waste resources and limit usability. For example, a user will not be able to hit the "back" button because they will have an outdated CSRF token, or if they try to resubmit a form with new values (such as after a validation error) it may not send.
Should it change per session or per request?
As discussed, it should change per session. The only time that a user should be given a new token per request is at login. This is to prevent a session fixation attack leading to a CSRF attack possibility.
For example: An attacker accesses the site and generates a new session. They take the session ID and inject it into a victim's browser (eg via writing cookie from a vulnerable neighbour domain, or using another vulnerability like jsessionid URLs), and also inject the CSRF token into a form in the victim's browser. They wait for the victim to log in with that form, and then use another form post to get the victim to perform an action with the still-live CSRF token.
To prevent this, invalidate the CSRF token and issue a new one in the places (like login) that you're already doing the same to the session ID to prevent session fixation attacks.
Should the client or the server set the CSRF token?
The server - always on the server! You want to generate the token from a trusted source, as per OWASP guidelines. This ensures that you know exactly where the token is generated and limits attack surface since an attacker cannot control what happens on the server.
What is the best strategy to apply CSRF protection?
I think CSRF is a very in-depth topic and can't really be summed up in just a few words. This is where a little research and reading can go a long way. I would recommend you take a look at the OWASP CSRF Prevention Cheat Sheet.
There are a lot of questions and answers about session fixation on StackOverflow, however, I am still confused about one thing. People often suggest that storing session in cookies is not enough to overcome the session fixation issue and you should rotate the session id after login. I can imagine that if you only use the session id to identify a user, you may still be vulnerable to an attack. However, I would like to ask about one specific case.
Let's assume that you use a signed cookie to store the whole session. On login you put into the cookie an id identifying the user. After logout, you delete the id. You do not change the session id, but as you change the session itself and you sign it, I cannot see any attack scenario taking advantage of this design. Is session fixation still an issue and, hence, is session id rotation still necessary in this case? If yes, can you provide an attack that could be used? Thank you.
The basics of Session Fixation is that it’s possible for an attacker to make the victim use a session that the attacker has access to. This is generally done by inducing the victim to use a certain session ID that is known to the attacker and doesn’t change after authentication.
Now what you’ve described doesn’t sound like a session identifier but rather a simple client-side data storage of user authentication data.
But nonetheless, if you make the cookie data dependent on data that again depends on the authenticated user which additionally is signed by the server, it certainly will change after authentication and won’t be known to the attacker. Then the security of that scheme depends on its actual implementation.
Why does Play Framework use [a signed version of the session id] as Cross Site Request Forgery (XSRF/CSRF) prevention token, rather than the session ID itself?
(With XSRF prevention token, I mean a magic value that must be included in a form submission, for the webapp to accept the form.)
If there's an eavesdropper s/he'll find both the XSRF token and the SID cookie anyway (?).
If there's an XSS exploit, then the malicious JavaScript code can read both the XSRF token and the SID cookie (?).
However:
An attacker cannot construct a valid XSRF token, given a SID, since s/he doesn't have the secret key used when signing the SID to obtain the XSRF token. -- But how could it happen that an attacker gets hold of only the SID, not the XSRF token? Is that far-fetched?
If the SID is sent in a HTTP Only cookie, then an attacker wouldn't have the SID even if s/he found the XSRF token, and perhaps the attacker really needs the SID? -- Is this far-fetched?
Code snippets:
Here Play constructs it's XSRF token (getId returns the session ID):
(play/framework/src/play/mvc/Scope.java)
public String getAuthenticityToken() {
return Crypto.sign(getId());
}
Here Play checks that a <form> has a valid XSRF token:
(play/framework/src/play/mvc/Controller.java)
protected static void checkAuthenticity() {
if(Scope.Params.current().get("authenticityToken") == null ||
!Scope.Params.current().get("authenticityToken").equals(
Scope.Session.current().getAuthenticityToken())) {
forbidden("Bad authenticity token");
}
}
Update:
Play has changed the way it generates XSRF tokens, now the SID is no longer used, instead a random value is signed and used! (I just updated my Play Framework Git repo clone from old Play version 1.1 to new 1.2. Perhaps I should have done this ... yesterday, hmm.)
public String getAuthenticityToken() {
if (!data.containsKey(AT_KEY)) {
data.put(AT_KEY, Crypto.sign(UUID.randomUUID().toString()));
}
return data.get(AT_KEY);
}
Well, then why did they do this change?
I found the commit:
[#669] Fix again and apply for Flash and Errors as well
d6e5dc50ea11fa7ef626cbdf01631595cbdda54c
From issue #669:
create session only when absolute necessary
A session cookie is created on every request of a resource. play should only create a session cookie if there is really data to be stored in the session.
So they're using a random value, not the SID, because the SID might not yet have been created. Well that's a reason not to use a derivative of the SID as XSRF token. But doesn't clarify why they signed/hashed the SID, in the past, when they were using it.
The first thing to say is that you can reuse the session ID as the CSRF token, insofar as it will protect you fine against CSRF and does not automatically create any serious security holes. However, for somewhat sound reasons, OWASP used to explicitly recommend against it. (They now don't address the question at all.)
The argument against reusing the session ID as the CSRF token can be summarized as follows (key points in bold, with justification beneath):
The session ID being acquired by an attacker is generally a more serious security breach than the CSRF token being acquired by an attacker.
All that an attacker gains from having the CSRF token (assuming that some other secure piece of information, like the session ID, hasn't been reused as the CSRF token) is the ability to perform CSRF attacks. This gives them two huge limitations that they wouldn't have if they actually acquired a session ID:
They still need to lure the user with the corresponding session token to an attack page (or have them read an attack email, or view an attack ad in an iframe, etc.) to exploit the CSRF token in any way at all. With the session ID, they'd just need to put it in their browser and then use the website as if they were that user.
While they can send requests using the user's credentials, the Same Origin Policy still prevents them from viewing the responses to those requests. This may (or may not, depending on the structure of the API you're protecting and the attacker's ingenuity) mean in practice that while the attacker can perform actions on the user's behalf, they cannot acquire sensitive information that the user is authorized to view. (Which of these you care more about depends upon the context - one assumes that an attacker would tend to prefer taking the contents of your bank account to merely knowing how much that is, but that they'd also rather know your medical history than vandalise it.)
The CSRF token is potentially easier for an attacker to acquire than the session ID
XSS attacks are likely to permit an attacker to acquire the CSRF token, since it's common practice to bake it into the DOM (e.g. as the value of an <input> element in a <form>. Session cookies, on the other hand, can be kept secret even in the face of a successful XSS attack using the HttpOnly flag, demanding more up-front work from an attacker to usefully exploit an XSS vulnerability.
If the CSRF token is being sent back to the server as a request parameter rather than a custom HTTP header (guaranteed to be the case when including it in ordinary HTML <form> submits), then web server access logs will generally log the CSRF token on GET requests (as it's part of the URL). Thus an attacker who manages to view the access log would be able to acquire many CSRF tokens.
Pages or scripts that the CSRF token is baked into may be cached in the user's browser, permitting an attacker to retrieve them from the cache (conceivably relevant after the user has, for example, used a public machine in a library or internet cafe, and then either cleared their cookies but not their cache, or used a 'Log Out' button that removes their session cookie from the browser without invalidating it server-side).
But if you're reusing the session ID as the CSRF token, then any attack that permits them to acquire the CSRF token automatically gives them the session ID as well.
Therefore you should not reuse the CSRF token as the session ID, since it makes the session ID more vulnerable.
To be honest, I kind of regard everything above as more of a theoretical concern than a practical one. The weak point in the argument is point 2; the only realistic vulnerabilities I can think of that could be used for acquiring CSRF tokens but not for acquiring session cookies are still really serious vulnerabilities. If you have an XSS hole on your site, or an attacker has access to your freaking server logs, chances are you're totally fucked anyway. And in most libraries and internet cafes I've been to, the staff were not security-savvy and it'd be pretty easy to install a keylogger undetected and just harvest passwords - there'd be no need for an attacker to go to the effort of waiting for people to use the machine and then ripping the contents of their browser cache.
However, unless your circumstances somehow make it difficult to store an additional random token for CSRF alongside the random session ID, why not just do it anyway for whatever modest security benefit it gives you?
A pure CSRF attack doesn't have access to the browser's cookies so when you say "eavesdropper", that's only going to be achievable if they're sniffing packets (i.e. no SSL, public wifi).
Depending on the configuration of the Play Framework (I'm not familiar with it so take this as general web app advice), the session and authentication cookies will almost certainly be flagged as HttpOnly so they they're unable to be read from the client via XSS.
Ultimately, the idea of using the synchroniser token pattern to protect against XSRF is to use a unique value (preferably cryptographically strong), known only to the server and the client and unique to that session. Based on this goal, Play Framework seems to do just fine.
Perhaps Play Framework doesn't want the SID in the HTML. An end user, Bob, might download a Web page, and if there's a <form> in that Web page, the SID would be included in the downloaded HTML (if the SID itself is used as XSRF token). If Bob then emails his downloaded page to Mallory, then Mallory would find the SID and could impersonate Bob!?
(Another minor reason not to use the SID: As I mentioned in my update, the SID might simply not be available. Perhaps it's generated as late as possible, to save CPU resources.)
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.
The question says pretty much everything. My point is, is the user able to change his cookie's values in order to appear "logged", when he's not really logged?
Cookies aren't secure. As others here have pointed out, you shouldn't trust any data received from the client, period. That said, cookies are often used to store Session IDs for logged in users, which is sort of what you're asking.
Signing your cookies will help you detect if they've been tampered with on the client. Basically, you create a HMAC of the keys/values and a secret key, known only to the server. On each request, you re-compute the MAC: if it matches the previous value, all is well; if not, you reject the request.
For more sensitive data, you can optionally encrypt the cookies. Most web frameworks will allow you transparently do these using some kind of "middleware" external to your application code, so the signing/validation and encryption/decryption happens for each request.
Also, you should know that simply securing your cookies doesn't guarantee, er...security :) You might still be vulnerable to Cross-site Request Forgeries.
For more information on cookies, check out this article.
So then if I change the user_id=1 do I become the administrator?
What if i type this into the address bar:
javascript:document.cookie=user_id=1&logged_in=true
In general it is a horrible idea to re-invent the wheal. Especially when it comes to security, a cookie should always be a very large random value. Whatever platform you are using should have a session handler already built for you.
Usually a server generated token is stored in a cookie. When the cookie expires the token is lost and the user needs to sign in again. You can't fake the token. It's not a boolean value stating whether the user is signed in or not.
Anything you get from the client (including cookies) is unsafe. The safe way is to set a cookie with a random hash, log the hash in the database together with an ID and a timestamp (and perhaps even IP) and then check the incoming cookies against the stored hashes. If you set the cookies to expire after some time, make sure you also reject them on the server if they arrive when they should not.