Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Is your web application really secure? ("CSRF")

by tinita (Parson)
on Mar 27, 2007 at 18:49 UTC ( [id://606832]=perlmeditation: print w/replies, xml ) Need Help??

This is about a kind of XSS security hole betterworld and I detected recently.
It's described at http://en.wikipedia.org/wiki/Cross-site_request_forgery.

Example

You have a webapp at yourdomain.example. Let's say you have a profile settings site where you can set your realname, for example (harmless example though).

The user is authenticated by a session cookie.
The form looks like this:

<form action="http://yourdomain.example/script" method="POST"> <input type="text" name="realname"> <input type="submit" name="submit.save_realname" value="Save"> </form>
The code looks like:
if ($cgi->param('submit.save_realname')) { my $name = $cgi->param('realname'); # do some checks on $name $user->realname($name); $user->update; }

Looks fine, doesn't it?

Missed something?

The form-method is POST. Why is it POST and why don't you care in the script if it really was sent by POST?
Consider some bad hacker creates a website on http://somewhere.example/bad.html:
<img src="http://yourdomain.example/script?submit.save_realname=1;real +name=owned" height="0" width="0" alt=""> or even: <meta http-equiv="refresh" content="0; URL=http://yourdomain.example/s +cript?submit.save_realname=1;realname=owned">
Now if you are logged in at your website (have a cookie) and somehow go to the bad website, your realname will be set automatically, and you canot do anything to prevent this. You don't even need Javascript.

An easy solution for this is to check for POST in your application.
if ($cgi->request_method() eq 'post')
(Bad thing is, there are a lot of websites out there that don't check, and while you can make your own applications safe, you can't do this for other sites you visit)

Still not safe?

Well, consider you have Javascript on. The bad website could look like this:
<body onLoad="document.forms[0].submit()"> <form action="http://yourdomain.example/script" method="POST"> <input type="text" name="realname"> <input type="submit" name="submit.save_realname" value="Save"> </form>
Here checking for POST will not help you (as a user). The only thing that helps is to have Javascript off for unknown websites (Site preferences in Opera, NoScript in FF). And hope that the bad html-form is not on a website where you have Javascript on.

Use Tokens

A lot of websites do this already; create a token (that expires like a session-id), and that must be a parameter in the form:
<input type="hidden" name="t" value="123abc...">
So every action of the script that changes data should require such a token. That makes your script pretty safe.

Couldn't browsers help me to surf more secure?

Well, if the scripts are programmed badly, why should browser vendors care?
Because they can.

In Opera you can say that you just want get cookies from the current site. Unfortunatley Opera still sends cookies from domain A to an embedded image of domain B.
In Firefox you can say "Privacy - Cookies - from original site only", which will prevent receiving and sending cookies.
But even the Firefox preference does not prevent doing a meta-refresh.

Also, there is no possibility to say "Warn me before a site does a form.submit() to a different domain". All these things could be implemented.

So, while you can do some things to make your own scripts safe, do you also think, browsers should take care of these issues? Here is a posting on Bugzilla https://bugzilla.mozilla.org/show_bug.cgi?id=375238 about this.

Update 2008-04-17: In Opera 9.5 the option "only send cookies to the site I visit" works reliably. In Firefox, the extension CookieSafe (and the option originalOnly) doesn't work reliably. So if you wanna be safe from CSRF, try Opera 9.5. But make sure you deactivate this option for OpenID sites.

Replies are listed 'Best First'.
Re: Is your web application really secure? ("CSRF")
by betterworld (Curate) on Mar 27, 2007 at 19:11 UTC
    A lot of websites do this already; create a token (that expires like a session-id), and that must be a parameter in the form:
    <input type="hidden" name="t" value="123abc...">
    So every action of the script that changes data should require such a token. That makes your script pretty safe.
    I do agree that embedding the session id or a token into the HTML code is currently the most secure and portable way to avoid XSRF attacks. However I regret that this takes much of the coolness and simplicity out of the concept of session cookies because they get kind of useless for POST requests. Moreover the expiration of the token or session id would render old browser windows useless.

    Other ways to avoid the problem are

    • Using Java script to embed the session cookie into a hidden field just before submitting
    • Using the referer

    However not every browser has Java script or referers turned on.

    Update: If you too think that browsers should do something about the issue, please consider taking part in the discussion on Bugzilla, which you will find referenced in tinita's original posting.

      However I regret that this takes much of the coolness and simplicity out of the concept of session cookies because they get kind of useless for POST requests.
      well, i think you're right, but IMHO a session cookie is most useful in GET requests, like viewing a forum thread or something else, so that you can bookmark it easily.
      if i had opened a website with a form in it, let the browser window open and try to send the form the next day i would consider it ok if i then get a message that the token is expired.
        if i had opened a website with a form in it, let the browser window open and try to send the form the next day i would consider it ok if i then get a message that the token is expired.
        If I don't lose my lengthy texts in the form, that would be all right.

        But consider you have 10 tabs open, and on the next morning, you re-login in one tab, and then go through the other 9 tabs one after another to click reload. This isn't exactly fun.

        Maybe the best (yet complex) solution is this: One could try to fix the expired tokens via Java script (you can read cookies with Java script). If the server still gets expired tokens (which means that Java script was not available), it could check the referer (and that the method was POST, so that no <img> attacks are possible). If there is no referer, the server can still return an error.

        This would make using the site more comfortable for those users who have either Java script or referers turned on, while the site is still usable for other users.

Re: Is your web application really secure? ("CSRF")
by Joost (Canon) on Mar 27, 2007 at 19:02 UTC
    I've thought for a while now that browsers probably shouldn't allow POST requests for another domain (especially scripted ones). Unfortunately that would break lots and lots of web applications so the chances of it being implemented are somewhere around zero.

    One thing that might help a bit is to set up your webserver to prohibit POSTs that don't have a referer header from your trusted site(s). I'm pretty sure there's a way to do that in apache.

    As far as I know you a malicious site can't fake a referer header* (unless maybe if you allow cross-site XMLHTTP - but all modern browsers prohibit that - right?)

    Good suggestion on the tokens, by the way.

      I've thought for a while now that browsers probably shouldn't allow POST requests for another domain (especially scripted ones). Unfortunately that would break lots and lots of web applications
      A good start would be to warn the user that the form is sent to an external site, and not to send cookies.

      As far as I know you a malicious site can't fake a referer header* (unless maybe if you allow cross-site XMLHTTP - but all modern browsers prohibit that - right?)

      Never trust the browser

      --MidLifeXis

Re: Is your web application really secure? ("CSRF")
by perrin (Chancellor) on Mar 28, 2007 at 04:39 UTC
      interesting. i tried it out, and it works. also interesting is, that many people don't seem to care and think it's nothing bad that somebody can put something in you shopping cart this way.
      i tried this out with a GET-form instead of post. even that works. so you can put anything into someones amazon shopping cart even without javascript. =(
Re: Is your web application really secure? ("CSRF")
by Spidy (Chaplain) on Mar 31, 2007 at 02:36 UTC
    Someone I know was working on a problem like this, and used something along the lines of the tokens solution that you described. What they did is to use a user's unique user ID value, pass it through crypt(), and then embed that into their form as the token. That value would then be checked with the user ID inside the database, and if the two matched any other checks could then be run.
      i would add a random string to it, because if you know that it's the crypted user id you can still attack a user. of course then a corrupted website would only work for one user at a time, so it's much safer than without tokens.
Re: Is your web application really secure? ("CSRF")
by Anonymous Monk on Jul 10, 2007 at 11:58 UTC
    Cool:)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://606832]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (7)
As of 2024-03-28 16:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found