views:

575

answers:

2

I have an website where people can place a vote like this:

http://mysite.com/vote/25

This will place a vote on item 25. I want to only make this available for registered users, and only if they want to do this. Now I know when someone is busy on the website, and someone gives them a link like this:

http://mysite.com/vote/30

then the vote will be places for him on the item without him wanting to do this.

I have read the explanation on the OWASP website, but i don't really understand it

Is this an example of CSFR, and how can I prevent this. The best thing i can think off is adding something to the link like a hash. But this will be quite irritating to put something on the end of all the links. Is there no other way of doing this.

Another thing can someone maybe give me some other example of this, because the website seems fairly fugue to me.

+1  A: 

This could become an example of CSRF if :

  • that link is fetched (via an <img> tag, for example) : forgery
  • from another site : cross-site


For example, if I could inject this <img> tag in the HTML source-code of stackoverflow (and I can, as stackoverflow allows one to use <img> tags in his posts) :

<img src="http://mysite.com/vote/30" />

You would just have voted for that item ;-)


The solution that is generally used is to place a token, that has a limited life-time, in the URL, and, when the URL is fetched, check that this token is still valid.

The basic idea would be :

  • When generating the page :
    • generate a unique token
    • store it in the user's session
    • and place it in the links of the page -- which would look like this : http://mysite.com/vote/30?token=AZERTYUHQNWGST
  • When the voting page is called :
    • Check if the token is present in the URL
    • Check if it's present in the user's session
    • If not => do not register the vote

The idea there is :

  • Tokens don't have a long life-time, and are hard to guess
  • Which means your attacker :
    • has only a window of a few minutes during which his injection will be valid
    • will have to be good at guessing ^^
    • will have to generate a different page for each user.


Also, note that the shorter the user's session remains active after he has left your site, the less risks there are that it's still valid when he visits the bad website.

But here, you have to choose between security and user-friendly...


Another idea (that's not perfectly secure, but helps against guys would don't know how to force a POST request), would be to only accept POST requests when people are voting :

  • The browser is sending GET requests for injected tags
  • As this URL is modifying some data, anyway, it should not work with GET, but only with POST

But note that this is not perfectly safe : it's (probably ? ) possible to force/forge a POST request, with some bit of Javascript.

Pascal MARTIN
You are right, it's about as easy to forge a GET as an POST request. Though I don't agree that it's necessary to have a token that expire. If the attacker are able to get hold of your session data you have bigger trouble than some extra votes. But your suggested fix still work since the key is just to have a token/random value in the Cookie and in the request data (saved in a cookie or tied to the users session key).
MyGGaN
Thank you both for the suggestions. I will change all the links to have this token. I have to agree this is a much saver method. However I am not going to implement the token expiration, i agree with MygGaN on this.
Saif Bechan
Any of you guys have an idea on what to do when I use AJAX for the voting. Should i just reuse the same key while the user is on the page, and only generate a new token when he refreshes. Or do i have to supply an new token for all the links whenever a vote is done.
Saif Bechan
Not sure there is a definitive answer to that question, but you have to think that refreshing the token "too often" can lead to troubles ; especially, what if the user has several tabs opened on your site in his browser, and the token changes from one of those ?
Pascal MARTIN
+2  A: 

First, GET request shouldn't be used to alter states on the server, so for you vote service I would recommend POST/PUT. This is only a guideline, but a cleaver one.

So to your question, CSRF is a client issue so it doesn't matter what kind of server language you use (PHP in your case). The standard fix is the same and goes like this: Have a random value in the URI/POST-data and the same value in the Cookie header. If those matches you could be sure there is no CSRF. There are a lot of info about how this could be done here on StackOverflow eg. this one.
Good luck!

MyGGaN