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.
Related
A legacy app I'm working on let the user fill in a ton of questions and save the answers in a batch at the very end of the questionnaire. The process is lengthy and a typical user may go through a timeout at some point.
The team has come up with the idea of an endless session to bypass that problem. After some Googling I found out many articles explaining how to increase the timeout; however I didn't come across articles exposing the risk of such practice. At first sight I find reasonable to set a timeout.
My questions are:
Do you think that an endless session may open up a security risk?
If so what are the typical risks incurred by that practice?
The main risk is that the session identifier effectively becomes the password if it never expires. Anyone with access to the session identifier could record this offline, and then use this at a later time to login to the application. For example, somebody could copy the session token from the cookie with brief access to a user's machine.
A mitigation for this is to routinely rotate session identifiers. Maybe you could have an AJAX request that fires off every 10 minutes and gets a new token for the current session - even with standard session expiration times (e.g. 10-20 minutes), this would be enough to keep the session alive so that it does not time-out before the form is submitted.
Brute forcing is not an issue: As long as the session identifier has enough entropy, then there is very little risk of this being brute forced. OWASP guidance here on selecting a strong method for session identifier generation.
More on the performance side than security side is that if you're storing objects in memory for each session, then eventually memory will fill as the number of sessions increase.
Another risk with long sessions is that any CSRF or XSS vulnerabilities have a long exposure time for exploitation. A short session timeout would mitigate any attack if the user visited a malicious site targeting your app, because the user would not be authenticated. Even with persistent login, this would be mitigated if you had a long term "refresh token" with a short term (i.e. session) "access token" if your site was sufficiently locked down (e.g. it only allows a request with CSRF protection itself to exchange a refresh token for an access token).
For example if there was an CSRF vulnerability:
[User] --> [Attacker's Site] --> [Your site]
Browser --> Malicious Page builds form for your site --> Submits form via AJAX
With an endless session timeout, this attack will succeed if the user has ever visited your site with their browser.
However if you had two session tokens: A refresh token and an access token and you required an access token to submit the form, this would prevent the attack. As the access token can only be retrieved by a same-site request, making sure that the handler for this request is sufficiently protected against CSRF would mitigate other vulnerabilities that may be present on your site.
Therefore if you must make the session infinite, use a different token that must be exchanged in order to authenticate with your website.
See this post for how to implement "remember me" functionality (aka our refresh token). The drawback is that you must implement the refresh token to access token logic yourself and require that the user must re-send any requests again with the access token (which can be implemented using client-side logic with JavaScript).
The main reason sessions have a time out is to being able to delete any server-side stored data associated to the session at some point. Otherwise your session storage would just always grow and you would never be able to remove any of that stored data at some point.
Granted, the longer a session lasts, the more likely an attacker may be able to guess a session’s ID and possibly hijack it. But that can easily be mitigated by changing the session’s ID now and then.
Endless sessions are not inherently less secure than your session implementation, but it does allow an attacker more time to exploit. For example, with an endless session, session hijacking becomes a little easier, as the attacker has unlimited time. It would also allow for brute-forcing of sessions, but given a sufficiently complexly generated session token, that shouldn't be an issue either.
Basically, it can/will make existing vulnerabilities easier to exploit, but will not weaken the implementation itself.
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!
My understanding is that CSRF prevents an attacker using an <img> tag to get the victim's browser to send a request that would be authenticated using the session cookie. Given that <img>s are always submitted using a GET request, not POST, then why is it necessary to require a CSRF token in a POST request?
Also, the attacker wouldn't be able to submit a form in the webpage without being able to run code (ie. an XSS attack), in which case they can circumvent your CSRF protections anyway.
The attacker can host a form on their own site, but it does not require the form to be submitted by the user. They can use JavaScript to do this:
<form method="post" action="http://www.example.com/executeAction">
<input type="hidden" name="action" value="deleteAllUsers">
</form>
<script>document.forms[0].submit()</script>
IFrame injection is more of a XSS vulnerability. A XSS vulnerability is more serious than a CSRF one because more damage can be done and it will always override any CSRF protection you have. Make sure you are always correctly encoding output for the context that the output is in (e.g. encode for HTML or for JavaScript as appropriate).
Check out the Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet - their best recommendation is to use the Synchronizer Token Pattern which seems similar to the link in your answer but can work in combination with cookies.
Also, here's a link to the XSS (Cross Site Scripting) Prevention Cheat Sheet.
Cross Site Request Forgery is when a site (let's say evil.example.com) can force a visiting user to make requests to another site (let's say example.com). It's not really forcing a user since embedding a image that (HTTP GET request) or POST request via form submission or javascript is not that difficult.
You should not make state or data changes via HTTP GET requests. img tags (get request) shouldn't be able to make any kind of change what so ever. If you allow this ... stop it. :)
POST requests need to contain a value that is not guessable by a remote attacker. Typically this is a per request random value.
So yes, CSRF is a a demonstrated, known vulnerability that you should bother protecting against.
Having done some further investigation:
It's possible for the attacker to host a <form> on their own site which submits to the target site (your site). All they need to do is get the victim to submit this form and it'll be submitted with their cookies and potentially their authentication.
It's also possible for the attacker to inject an <iframe> into your site, which would then be able to display this malicious <form>.
I'm thinking that a token-based approach is a better solution for my use case.
to prevent CSRF attacks, a random CSRF
secret has been generated.
The above is from symfony:
http://www.symfony-project.org/getting-started/1_4/en/04-Project-Setup
Since it's finally operated by users,which is so called deputy attack.how can it work by setting that secret?
OWASP (open web application security project) has very good explanation on CSRF, I encourage you to read it and post your questions afterwards.
http://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
If you are looking for sample implementation on how to prevent CSRF, take a look at Django and its write-up.
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/
CSRF or XSRF stands for Cross Site Request Forgery. The idea is that the attacker is "forging" a HTTP request when a victim executes html or javascript created by the hacker. Here is an example CSRF exploit I wrote against XAMPP. The idea is that this html/js is building a POST request which "rides" on already existing session. The CSRF exploit must be executed by the browser of an XAMPP administrator that is currently logged in.
<html>
<form action='http://127.0.0.1/security/xamppsecurity.php' method='POST' id=1>
<input type="hidden" name="_SERVER[REMOTE_ADDR]" value="127.0.0.1">
<input type=hidden name="xamppuser" value=admin >
<input type=hidden name="xampppasswd" value=password>
<input type=hidden name="xamppaccess" value="Make+safe+the+XAMPP+directory">
<input type=submit>
</form>
</html>
<script>
document.getElementById(1).submit();
</script>
In order to do this the hacker must know a lot about the request ahead of time, most importantly the destination server and all of the variables. The hacker does NOT need to know the sesion id or the "basic-auth" header, this is automatically provided by the browser. If you add a randomly genearted secret then the request cannot be forged unless the hacker knows that value. Its like having a password for every request you send to the server. A hacker CAN obtain this token value using XSS. This is a more complex attack, but here is an exploit that bypass token based CSRF protection using XSS: http://www.milw0rm.com/exploits/7922
Try readng the CSRF FAQ from cgisecurity ( http://www.cgisecurity.com/csrf-faq.html ). When you have questions clarifying the FAQ, we'll be happy to clarify.
EDIT:Quoting for the CSRF FAQ, linked previously,the section that discusses the random secret:
What can I do to protect my own applications?
The most popular suggestion to preventing CSRF involves appending challenge tokens to each request. It is important to state that this challenge token MUST be associated with the user session, otherwise an attacker may be able to fetch a valid token on their own and utilize it in an attack. In addition to being tied to the user session it is important to limit the time peroid to which a token is valid. This method is documented in multiple documents however as pointed out in mailing list postings an attacker can utilize an existing browser vulnerability or XSS flaw to grab this session token.
The CSRF secret in Symfony is well explained here: http://www.nacho-martin.com/csrf-tokens-in-symfony
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.