I have a web application which has basic authentication - username, password, session and stuff. However I have a specific need to prevent users from spoofing the POST requests (even for logged in users). In my application, I am specifically validating the user's session before accepting the POST data (taking care of XSS and stuff also).
Like:
if(user session exists)
{
// handle the data POSTed
}
else {
// ...
}
I am storing the sessions IDs in the database. Is there anything else I should be aware of to prevent bogus POST requests or is this sufficient?
I am specifically validating the user's session before accepting the POST
If you mean what is normally meant by ‘session’: a persistent token stored in a cookie that identifies the user and associated session data, then no, that's not enough. That cookie is sent automatically by the browser even if the POST request was provoked by another (attacker) site.
The keyword you are looking for here is Cross-Site Request Forgery or XSRF, where an authenticated user can be made by an attacker (via scripting or other methods) to make a GET or POST request to your site. Such requests are not generally distinguishable from legitimate requests. (Some people try to do so though checking the HTTP referrer data, but this is unreliable.)
These attacks are not quite as immediately damaging as server-side (SQL, command) or client-side (HTML, JavaScript) injections, but they are more common than both: few web programmers both to include the proper countermeasures, unfortunately. Until they get their sites compromised by an XSRF hole anyway.
There are various way to defend against XSRF, but the only really effective approach is to include in each submittable form a secret value that the third-party attacker won't know. This is often known as a post key, as mentioned by Eimantas.
There are various ways to generate such secret information. A simple approach is to add a randomly-generated code to each user's account details, then put that in a hidden field in the form and check for its presence in the submission. eg in PHP:
<form method="post" action="delete.php"><div>
<input type="hidden" name="key" value="<?php echo(htmlspecialchars($user['key'])); ?>"/>
<input type="submit" value="Delete" />
</div></form>
if ($_POST['key']!=$user['key'])
// error
An attacker won't know the key for that user so can't make a link/form that contains it.
You could also use a cryptographic hash function on the user's ID with a server-secret key, rather than keeping a separate code. With a hash, you can also throw in other stuff like an expiry time so that forms have to be submitted within a certain timeframe. Or you can generate a one-use transaction key, which you can also use to make sure you can't submit the same form twice (for stopping double-posting).
You could try generating post keys for each post request. Sort of additional param that shows that post request is valid and was executed from a form on your page.
If you are building valid POST requests in Javascript in the user's browser, there is not much you can do to prevent a determined user from submitting bogus POSTs to your server. The user has a valid session id that he can use to make a POST request. He also has access to all of the code and all the other data that code has access to for building the request.
You can't rely on browser-side code to secure your system. The security has to be enforced at the server. For example, all operations on objects should be authenticated and authorized.
Use a CAPTCHA image.
The web is built on REST, which by definition is all about transferring state from one point to another. Someone with enough time on their hands could craft a POST request that emulates an active session.
Like all secure requests, CAPTCHA is validated server-side.
In my current application I have some code which is sent to the browser and the browser then posts back and must not be able to modify it. What I do is to append a secret string to the value, get the SHA1 checksum of that full string, and then require the browser to send back both the value and the checksum. I'm pretty sure this is how .NET does viewstate, too.
If user session is long-lived, you are still susceptible to XSRF. You need to take measures against that too.
If you are on .NET, check out AntiForgeryToken,
http://msdn.microsoft.com/en-us/library/dd492767.aspx
When accepting user input, the zero-level thing you need to do, before storing things in the database is to make sure you run the data via the mysql_real_escape_string($MyPostData) function.
Also, it is good for every variable/data you want to accept via POST to validate it programmatically based on its type and what you intend to do with it.
These are two main rules for making sure there's no 'funny' business coming from the user: making sure you work with valid variables AND making sure data that gets to the database is verified and escaped properly.
With your model (and especially if you use integer numbers for your session IDs) an attacker can easily submit a request on behalf of another user (e.g. decrement your own session ID and you are already someone else provided this session ID exists).
You need to have a unique session key/guid associated with each session ID and stored both in the DB and on the client in the cookies. Each time your client submits a request you should read not only session ID but also the session GUID and then validate them both against your database values.
In addition to that you may also want to consider some XSRF mitigation strategies.
Related
Some months ago, I visited a security workshop and we discussed some securitiy problems when using cookies for session management.
I was told, that cookies originally were not designed for handling sessions.
But how should it be done then?
A secure way to do this would be to generate a cryptographically random 128 bit value (that is a random value generated by a CSPRNG), and then pass this as POST data to each page.
e.g.
<form method="post" action="/globalHandler">
<input type="hidden" name="sessionId" value="<sessiontoken>" />
<input type="hidden" name="page" value="accountDetails" />
</form>
The advantage is that the session identifier never needs to be transmitted in a cookie, mitigating SSL attacks such as POODLE or BREACH (the attacker has no way of injecting requests because they do not have the session identifier). This also inherently protects against CSRF attacks.
The disadvantage is that every page that is to be accessed whilst logged in will need to be accessible via the POST method only, where the suitable validation can take place on the sessionId parameter. Therefore it is best done to a website when it is first developed, rather than altering an existing website to fit this format.
Using POST data is more secure than GET, because with GET the details would be in the query string portion of the URL. e.g.
https://example.com?sessionId=1234...
Not only does this make the session identifier visible on the user's screen, but it can also be leaked by the referer header, and will also be logged by default in browser history, proxy and server logs. POST data is rarely logged by default.
Some banks use this method to ensure that there is only one active path made by the user during their session - the session identifier can easily be rotated so that if a user goes down a different route, their identifier does not match and they are logged out. This is useful from a security point of view when you have a multi-step process that must be followed in a set order. Some business logic vulnerabilities can otherwise arise when a user takes a different path than that of the developers' expectations.
Cookies are still the best way for session management. Just be aware of the limitations in cookies. For better results, use Secure Cookies that cannot be transmitted over http, only https. These are harder to accidentally leak, if, for example, there is a reference to an unsecured image on the page.
What are the advantages and disadvantages of placing session id in a hidden form input vs a cookie?
Is it correct to put CSRF-Tag in a hidden form input field and session id in an httpOnly cookie? Which is more secure?
If you put Session ID in a hidden form field, that is a lot more secure, however it can hamper the user experience.
The reason is that is this would inherently protect you against CSRF because any cross-domain requests made to your site will mean that the browser will not automatically include the session identifier that makes CSRF attacks possible. It also neutralises session fixation attacks as there is no cookie to poison. Additionally any Login CSRF is also dead in the water.
To implement this, you would have every action on your site, including navigation, to be actioned via the POST method. The GET method would be unsuitable because this would expose the session identifier in the browser history, in any proxy or server logs by default, and can also be leaked via the referer header.
For example,
<form method="post" action="/executeAction">
<input type="hidden" name="sessionId" value="12345678901234567890" />
<input type="hidden" name="action" value="navigateToAccountStatus" />
</form>
Note that this will prevent use of the back button without the user re-submitting the form (which could be dangerous if the action wasn't a safe action). To guard against this, you could refresh the session identifier after each action is processed.
Another reason is this will protect your site against attacks such as POODLE. As there are no cookies for a Man-In-The-Middle to brute force one byte at a time from, a POODLE attack would be fruitless.
Note that this approach is more difficult to implement, and not many web-frameworks support it as default.
Is it correct to put CSRF-Tag in form hidden field and Session Id in httpOnly cookie?
Yes, this is the approach most sites take. It is "secure enough" for most purposes - only very high security systems like online banking should take the form approach.
I don't think that one is inherently less secure than the other. Security is generally built in layers. By asserting that choice A can be more secure than choice B, when both choices play on the same vertical, you are asserting that security stops there. This is completely false and unsubstantiated in practice.
By passing around session ids primarily in the form of hidden form inputs you actually create more problems than you solve for yourself. Also, I disagree with the assertion that this in anyway makes you inherently protected from CSRF.
When you think about what purpose a session serves (retaining state between the server and client over an otherwise stateless protocol), it doesn't actually make sense to say I will pass all of my session ids via hidden input fields. Because, for one, not every request made to your server involves the use of a form. For another, the state is lost the moment the user refreshes the page or closes their browser. This isn't pragmatic at all.
It is correct to place CSRF tokens in hidden inputs. It's also not incorrect to send them along to the client via HTTP headers. The CSRF token by itself isn't enough to prevent the attack. What's also needed is that the server understands how to recognize that this toke, which was supposedly uniquely generated for this client, is not reused and not tied to another session by the same user.
Since generally a CSRF attack is based on the premise that you cannot distinguish the real user from the malicious forgery, the idea is to make the forger's job more difficult by regenerating the token for every request. Coupled with a use-only-once requirement and it doesn't actually matter anymore that the session is hijacked. So you really shouldn't try to solve this problem at the wrong level, by assuming that you can somehow solve both problems by relying on passing your session ids in hidden inputs and convincing yourself that this is more secure than storing the session id in a cookie. It's not. There should be additional layers of security to protect your sessions from session hijacking or session fixation attacks like using SSL only cookies, HSTS, and regnerating session ids (while deleting the old session files) upon re-authentication requests. Also, forcing re-authentication for user-level non-idempotent actions.
But please please don't assume that hidden input makes you inherently more secure from CSRF or Session Fixation, or any of these attacks. It doesn't!
So I just read this article by Jeff Atwood and I wanted to make sure I understand it correctly as to how it applies to my use case. I am trying to validate a session for silent login. For security purposes this should be done with a POST right? Does it matter? I am just passing the sessionID and username from the cookie.
When it comes to CSRF (Cross-site request forgery), you can cause a user to take any action on any site which they are logged in to provided that the action requires only a GET. Forcing this to be done over a POST request defeats the approach of embedding an image, script tag, whatever in another page.
Even POST isn't completely secure in this scenario. There are other ways to mount a CSRF attack on a site using POST. Clickjacking/UI-Redressing enables another site to trick a user into submitting a form to a different website.
Basically the best way to validate is to add an automatically generated, hidden form element. You can store this inside your session data (Example: $_SESSION for PHP) so that you only need to generate a token at the start of a session. Of course, an attack could try do something like clickjacking (mentioned above) in combination with a iframe pointing directly to your site and possibly some JS to hide things a little.
For anything important you should re-prompt the user for their password, thereby greatly diminishing the value of any successful CSRF attacks.
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.
Most web sites you can log on to also provide the feature so it remembers you between sessions.
What's the accepted and secure techniques for implementing that ? (What do you put in the cookies and how do you handle it on the server/db?)
This recent 2009 chapter in Spring Security 3.0 discusses Remember-Me type authentication. The general concepts are not specific to Spring Security so you should be able to benefit from it even if you are not using it. The chapter also cites a Barry Jaspan's 2006 blog posting which is an improvement over the techniques described in Charles Miller's 2004 blog posting.
The blog entry basically comes down to:
When the user successfully logs in with Remember Me checked, a login cookie is issued in addition to the standard session management cookie.
The login cookie contains the user's username, a series identifier, and a token. The series and token are unguessable random numbers from a suitably large space. All three are stored together in a database table.
When a non-logged-in user visits the site and presents a login cookie, the username, series, and token are looked up in the database.
If the triplet is present, the user is considered authenticated. The used token is removed from the database. A new token is generated, stored in database with the username and the same series identifier, and a new login cookie containing all three is issued to the user.
If the username and series are present but the token does not match, a theft is assumed. The user receives a strongly worded warning and all of the user's remembered sessions are deleted.
If the username and series are not present, the login cookie is ignored.
Signed cookies that can not be tampered with can be a good idea when you don't require a whole server-side state ... lean mean and efficient.
You still run the risk of cookie theft but you can always sign the cookie using IP address, User-agent and other things to help minimize the threat.
It's just a cookie with a long life value assigned. However it will only work so long as the cookie exists. For example, I have my Firefox set to clear my cookies when I close the browser. So this wouldn't work for me.
Cookies, but the user can decide to delete it.
In the same spirit there is some kind of solution, using Flash. Flash can store informations on the client-side, not a cookie, it's not erased (usually) by the browser. With it, you can remeber which user is asking for pages, but you're stuck using a plugin-using solution, and need to know Flash..
I don't see any other solutions.
Do not try to implement session cookies yourself.
Most web frameworks give you an abstraction over this, leaving you care-free about the many security issues you might be exposing yourself to.
A simple API in pseudo-code in a web framework might look something like this, on login:
authFrwk.loginUser(request.POST.get(username), request.POST.get(password));
This will return a cookie to the client (handled exclusively by the framework).
A securely authorized operation will look something like this:
if (authFrwk.isLoggedOn()) // implicitly checks user session cookie
doSomethingImportant();
else
return notLoggedInMsg();
Basically, a session cookie is given a unique ID on the server-side, which a malacious user cannot generate/guess by himself, and which identifies the client as a logged-on user.