Page 1 of 1

Ajax and CSRF vulnerability

Posted: Tue Jul 14, 2009 7:05 am
by jacky
Hi,

Working on a new project, I have recently been reading about security issues as I obviously want my application to be as secure as it can be. I was aware of issues when it comes to SQL injections and XSS a little, and I think I should be relatively safe on that side, using a function like the following one before treating any data from the "outside" :

Code: Select all

function ensure_type($type, $value)
{
    switch ($type)
    {
        case 'string':
            $value = htmlentities(trim((string)$value));
            break;
        case 'bool':
            $value = ($value) ? TRUE : FALSE;
            break;
        case 'int':
        default:
            $value = (int)$value;
            break;
    }
    return $value;
}
However I didn't know much (or anything) about CSRF and I've been doing some reading, trying to understand how it works and how to prevent it.

I found that my app was not protected against such attacks at the moment, and decided to add the use of what I call a "session token." The idea would be that any request would need to have a valid session token include to be treated, otherwise resulting on a "invalid session" error. This token would be a randomly generated hash stored on server for like 15 minutes.

So when a user visits the site, he would first asks for a new session token, which then would be included in later requests. If a request doesn't have such a token, or after 15 minutes of inactivity and that token has been "dropped" by server/became invalid, no action could be done without first asking for a new session token to be generated. That way, for a request to be allowed to do some action, the server would need a valid POST request with the right user-authentication cookie as well as a recently server-generated session token, which from what I understand should prevent most (or a good deal) of possible CSRF attacks.

However, when thinking about it and how I would implement dealing with this whole thing for my users, it seemed to me that if an attacker was able to manage to get a user of the site to click on a malicious link from the attacker's page or something (which is pretty much at the start of all/most CSRF attacks if I got it right), it could still be "bypassed" using a little Ajax : when the user clicks on the link, an Ajax request is generated to the server, asking for a new session token. It then gets said token, and sends another request which then, unless I'm missing something, could be POST, have a valid session token, and since generated by Ajax from the user's browser would include the user's cookies for my site... in other words, on my end (server) it looks like a perfectly valid request that should be processed.

Leading to my question : am I wrong/missing something in that scenario, making it actually impossible to happen ? And if not, I can't seem to find of a good way to prevent that, since it can't come (from what I'm seeing) from either a cookie of a recently generated random hash (my "session token"), so what else could/should I do?

Thanks for any help/information,
-jacky

Re: Ajax and CSRF vulnerability

Posted: Tue Jul 14, 2009 1:29 pm
by Weirdan
It then gets said token,
This can't happen due to the same origin policy. In the context of your webpage (originating from your [malicious] domain) you can make users' browsers issue requests to another domain, but you can't read the response.

Re: Ajax and CSRF vulnerability

Posted: Tue Jul 14, 2009 3:02 pm
by jacky
Weirdan wrote:This can't happen due to the same origin policy. In the context of your webpage (originating from your [malicious] domain) you can make users' browsers issue requests to another domain, but you can't read the response.
Oh right, looking more into it I see where I got things wrong -- thanks, makes more sense now. :)

So the use of such a "session token" should be enough to protect from most attacks, good.

Re: Ajax and CSRF vulnerability

Posted: Fri Jul 24, 2009 11:39 am
by kaisellgren
The code you got there is not very useful. There's not much point of making general functions to "protect" some data. If you want to insert binary data into your database for instance, then you can't use your ensure_type() function at all. There are so many different situations and your htmlentities() function will not protect from XSS (that was its purpose, right?).

As what comes to CSRF, a simple and an effective way to prevent CSRFs is to generate a pseudo random token, save it in the $_SESSION, and output it in the form or links (URI). When a request is processed, you match the submitted token against the one in the session. There's no real need for you to think about expiration here; the token dies along with the session in a sufficiently short time.

Re: Ajax and CSRF vulnerability

Posted: Fri Jul 24, 2009 12:46 pm
by jackpf
Sorry to go a bit off topic...but how does using a random string prevent csrf?

Obviously it'll prevent people from just making a simple form on their own site, and submitting it to your site, but using something a bit more advanced, like cURL or sockets, you could easily request the page, save the session cookie somewhere, and then submit the form with the right unique id, and send the session ID as well. Wouldn't that appear to be a valid request?

Re: Ajax and CSRF vulnerability

Posted: Fri Jul 24, 2009 12:57 pm
by kaisellgren
jackpf wrote:Sorry to go a bit off topic...but how does using a random string prevent csrf?
It has to be as random as possible so that no one can predict it.
jackpf wrote:using something a bit more advanced, like cURL or sockets, you could easily request the page, save the session cookie somewhere, and then submit the form with the right unique id, and send the session ID as well. Wouldn't that appear to be a valid request?
If the token is strong (has enough randomness and length), you can't know do the request. You can't have that "right unique id". And ah, you wouldn't use cURL for CSRF... you would need to have the session identifier, too, and it becomes session forging rather than CSRF at that point.

Re: Ajax and CSRF vulnerability

Posted: Fri Jul 24, 2009 4:37 pm
by jackpf
Fair enough :)

Re: Ajax and CSRF vulnerability

Posted: Fri Jul 24, 2009 8:50 pm
by Weirdan
jackpf wrote:Sorry to go a bit off topic...but how does using a random string prevent csrf?

Obviously it'll prevent people from just making a simple form on their own site, and submitting it to your site,
That is CSRF in its essence.
jackpf wrote:but using something a bit more advanced, like cURL or sockets, you could easily request the page
That's misunderstanding the point of CSRF. When you make "simple form on your own site" you can make your visitor (authenticated at that site you're targeting) do some actions he is not aware of. When you do that through curl, you're doing it yourself. That's a world of difference.

Re: Ajax and CSRF vulnerability

Posted: Sat Jul 25, 2009 5:38 am
by jackpf
Ahh I guess I was a bit mistaken about CSRF.

Thanks for explaining.

Re: Ajax and CSRF vulnerability

Posted: Sat Jul 25, 2009 4:52 pm
by jacky
kaisellgren wrote:The code you got there is not very useful. There's not much point of making general functions to "protect" some data. If you want to insert binary data into your database for instance, then you can't use your ensure_type() function at all. There are so many different situations and your htmlentities() function will not protect from XSS (that was its purpose, right?).
The idea isn't to "protect" data, but to ensure "its type". As in, when I expect an integer, it will make sure I have one and not anything else, for any and all data coming from "outside" and that, as such, might have been altered.

And any string will be processed through htmlentities() so that one cannot use HTML tags and whatnot since that could generate some security issues later on when showing such data. E.g. one cannot have enter a <script> tag in a place where the text is meant to be later show to uses (like a post, signature, etc), which I thought was a good way to prevent some XSS attacks.

I fully realize it is not perfect nor does it covers all possible cases, but it should cover my needs (for instance I never have to store binary data into the DB). And I also have an is_valid() function to ensure that what should be an IP, an email, etc indeed "looks like" one, which should also prevent some problems I believe.

Are there other things I should do/test for in order to protect myself/users from XSS?
kaisellgren wrote:As what comes to CSRF, a simple and an effective way to prevent CSRFs is to generate a pseudo random token, save it in the $_SESSION, and output it in the form or links (URI). When a request is processed, you match the submitted token against the one in the session. There's no real need for you to think about expiration here; the token dies along with the session in a sufficiently short time.
Yes this is what I've implemented now, as I described earlier. I mentioned the expiration because I didn't actually (want to) use $_SESSION, and instead compare the token sent by the user in the request with the one stored on the server, which has an "expiration date," that is all.

Re: Ajax and CSRF vulnerability

Posted: Sun Jul 26, 2009 12:57 am
by kaisellgren
jacky wrote:The idea isn't to "protect" data, but to ensure "its type".
jacky wrote:And any string will be processed through htmlentities() so that one cannot use HTML tags [...] which I thought was a good way to prevent some XSS attacks.
So, it's not type verification after all. :P
jacky wrote:Are there other things I should do/test for in order to protect myself/users from XSS?
Well, that piece of code you provided barely protects from XSS. You should supply three parameters to htmlentities(). After that, it eliminates XSS if you output inside elements.
jacky wrote:I mentioned the expiration because I didn't actually (want to) use $_SESSION, and instead compare the token sent by the user in the request with the one stored on the server, which has an "expiration date," that is all.
Yeah you could do that, but I don't see a reason not to use built-in sessions. A few good rules to follow when it comes to CSRF:
- Have the token in the session because then the token dies along with the session. This way you don't have to deal with expiration.
- Use strong tokens. A token should be at least 64-bits strong (in my opinion), but ideally as strong as the session identifier.
- Prefer POST tokens over GET tokens. Use hidden form fields or AJAX with POST instead of placing them in the URI. Browsers keep tokens in their history (CSSHH). Browsers submit tokens in referrers. POST tokens do not create ugly URIs.
- Block requests that contain entirely wrong referrers.
- Beware of login, log-out and file upload CSRF. These are easily forgotten. CSRF can happen anywhere.
- Limit session lifetime.

I found out a way to protect from CSRF by the use of cookies. However, I recommend using the standard token approach.

Re: Ajax and CSRF vulnerability

Posted: Mon Jul 27, 2009 12:05 pm
by jacky
kaisellgren wrote:
jacky wrote:The idea isn't to "protect" data, but to ensure "its type".
jacky wrote:And any string will be processed through htmlentities() so that one cannot use HTML tags [...] which I thought was a good way to prevent some XSS attacks.
So, it's not type verification after all. :P
I guess not ;) It worked for me cause I saw it as being "string" and not HTML or something, I guess...
kaisellgren wrote:Well, that piece of code you provided barely protects from XSS. You should supply three parameters to htmlentities(). After that, it eliminates XSS if you output inside elements.
uh, I don't get that... calling htmlentities() like I do only uses the default parameters, what would it change that I actually specify them? The resulting behavior should be exactly the same? Or are you saying the default values aren't good? (and if so, why? what should be used instead?)
kaisellgren wrote:Yeah you could do that, but I don't see a reason not to use built-in sessions.
Well, it's just that I already have in my app an easy way to - on the server - store values temporarily, and it allows me things like "linking" the session token to the user and its IP, so changing IP will "invalidate" the session. Plus, and while it doesn't mean anything here, I also liked it better to compare the two tokens with only one of them coming from the user.
kaisellgren wrote:- Block requests that contain entirely wrong referrers.
Good idea, haven't done that yet, but will do...

Thanks for all your help/comments!
-jacky

Re: Ajax and CSRF vulnerability

Posted: Sun Aug 02, 2009 1:41 am
by kaisellgren
Sorry for the late reply.
jacky wrote:uh, I don't get that... calling htmlentities() like I do only uses the default parameters, what would it change that I actually specify them? The resulting behavior should be exactly the same? Or are you saying the default values aren't good? (and if so, why? what should be used instead?)
When calling htmlentities() or htmlspecialchars() to protect from XSS, you need to specify the second parameter as ENT_QUOTES. The third parameter (encoding) must be the same as the one you use for your site.
jacky wrote:it allows me things like "linking" the session token to the user and its IP, so changing IP will "invalidate" the session. Plus, and while it doesn't mean anything here, I also liked it better to compare the two tokens with only one of them coming from the user.
I don't understand how you could not do this with the standard Session system. Store the IP in the session after a successful logon, and compare the current IP to the one in the session on each page load. That's the simplest way to do it.