Preventing CSRF and XSRF Attacks

October 14, 2008

In Cross-Site Request Forgeries and You I urged developers to take a close look at possible CSRF / XSRF vulnerabilities on their own websites. They're the worst kind of vulnerability -- very easy to exploit by attackers, yet not so intuitively easy to understand for software developers, at least until you've been bitten by one.

On the Freedom to Tinker blog, Bill Zeller offers one of the best, most concise explanation of XSRF that I've read to date:

CSRF vulnerabilities occur when a website allows an authenticated user to perform a sensitive action but does not verify that the user herself is invoking that action. The key to understanding CSRF attacks is to recognize that websites typically don't verify that a request came from an authorized user. Instead they verify only that the request came from the browser of an authorized user. Because browsers run code sent by multiple sites, there is a danger that one site will (unbeknownst to the user) send a request to a second site, and the second site will mistakenly think that the user authorized the request.

That's the key element to understanding XSRF. Attackers are gambling that users have a validated login cookie for your website already stored in their browser. All they need to do is get that browser to make a request to your website on their behalf. If they can either:

  1. Convince your users to click on a HTML page they've constructed
  2. Insert arbitrary HTML in a target website that your users visit

The XSRF game is afoot. Not too difficult, is it?

Bill Zeller and Ed Felten also identified new XSRF vulnerabilities in four major websites less than two weeks ago:

  1. ING Direct

    We discovered CSRF vulnerabilities in ING's site that allowed an attacker to open additional accounts on behalf of a user and transfer funds from a user's account to the attacker's account.

  2. YouTube

    We discovered CSRF vulnerabilities in nearly every action a user can perform on YouTube.

  3. MetaFilter

    We discovered a CSRF vulnerability in MetaFilter that allowed an attacker to take control of a user's account.

  4. The New York Times

    We discovered a CSRF vulnerability in NYTimes.com that makes user email addresses available to an attacker. If you are a NYTimes.com member, abitrary sites can use this attack to determine your email address and use it to send spam or to identify you.

If major public facing websites are falling prey to these serious XSRF exploits, how confident do you feel that you haven't made the same mistakes? Consider carefully. I'm saying this as a developer who has already made these same mistakes on his own website. I'm just as guilty as anyone.

It's our job to make sure future developers don't repeat the same stupid mistakes we made -- at least not without a fight. The Felten and Zeller paper (pdf) recommends the "double-submitted cookie" method to prevent XSRF:

When a user visits a site, the site should generate a (cryptographically strong) pseudorandom value and set it as a cookie on the user's machine. The site should require every form submission to include this pseudorandom value as a form value and also as a cookie value. When a POST request is sent to the site, the request should only be considered valid if the form value and the cookie value are the same. When an attacker submits a form on behalf of a user, he can only modify the values of the form. An attacker cannot read any data sent from the server or modify cookie values, per the same-origin policy. This means that while an attacker can send any value he wants with the form, he will be unable to modify or read the value stored in the cookie. Since the cookie value and the form value must be the same, the attacker will be unable to successfully submit a form unless he is able to guess the pseudorandom value.

The advantage of this approach is that it requires no server state; you simply set the cookie value once, then every HTTP POST checks to ensure that one of the submitted <input> values contains the exact same cookie value. Any difference between the two means a possible XSRF attack.

An even stronger, albeit more complex, prevention method is to leverage server state -- to generate (and track, with timeout) a unique random key for every single HTML FORM you send down to the client. We use a variant of this method on Stack Overflow with great success. That's why with every <form> you'll see the following:

<input id="fkey" name="fkey" type="hidden" value="df8652852f139" />

If you want to audit a website for XSRF vulnerabilities, start by asking this simple question about every single HTML form you can find: "where's the XSRF value?"

Posted by Jeff Atwood
76 Comments

Sounds like a good reason for security wingnuts to finally start accepting cookies

Gwyn on October 15, 2008 3:56 AM

Can we PLEASE add an HTTP header to indicate image and other embeded requests? While it wouldn't prevent all XSRF attacks, it'd certainly go a long way to putting some sanity in the img tag.

David on October 15, 2008 4:06 AM

Of course form value and the cookie value are the same should not be taken literary, but as:

X - form value,
C - cookie value
bool F(X,C) - validation function, which can be as simple as X == C, or as complicated as you wish F(X,C) = F3(F1(X), F2(C))

Busy on October 15, 2008 4:14 AM

Not just forms. Any Ajax request should also contain a pseudo-random value (I think it's called a nonce)

Matt on October 15, 2008 4:16 AM

I'd rather we spent time on tougher laws for punishing people that exploit the perpetually weak internet, than waste time plugging every hole in the sinking ship. The fact that there's jail time involved in break enter lets me sleep far more soundly than the shiny new locks and bulletproof doors I just installed in my home.

But then again, I guess everyone is responsible for protecting their own interests.

Hey I'm from Canada, where everyone loves everyone and we didn't invent the internet.

Hoser on October 15, 2008 4:26 AM

Over the last 3 weeks I've read a number of posts regarding CSRF attacks. It's the first time I've heard of them. Luckily, I'm about to start a personal project for a government department which maintains information for approx 10,000 disabled clients.

I don't think they'd appreciate too much exposing the names and addresses of that many vulnerable people.

This just highlights that there is SO much developers need to be aware of. Especially those just out of Uni beginning their career. It isn't just a simple application with a login form and CRUD operations. There's major security concerns. User validation. Data validation. Data security. Control flow checks. What would take 1 or 2 weeks as a school assignment can take 6 months to do properly and securely.

`Josh on October 15, 2008 4:30 AM

I want to vote this post up :)
not addicted at all...

That is the best explanation I've ever read of them.

Amen to mandatory cookies, but there are a whole lot of people out there who have been told that cookies are bad, mmmkay, and just won't budge. Their brother in law, who knows enough to get their printer unjammed, told them so.

No problem with adding toolbars to their browsers because they get free email smileys though...

seanb on October 15, 2008 4:41 AM

@David: if you're performing actions in response to GET requests, you're doing it wrong. It's not just a matter of security--you'll end up with Google Web Accelerator deleting your site ( http://shiflett.org/blog/2006/dec/google-web-accelerator-debate ).

See http://en.wikipedia.org/wiki/Post/Redirect/Get for the One True HTTP interaction model.

Braden on October 15, 2008 5:10 AM

Sounds like a good reason for security wingnuts to finally start accepting cookies.

No, because its the cookie that authenticates the browser and not the user. CSRF attacks cannot be accomplished without the cookie. If you keep no cookies, you cannot be targeted.

pegr on October 15, 2008 5:10 AM

@Jason: Yes, browser cross-site restrictions prevent access to most content from another domain--they can submit a form they create to your handler, and they can currently make the user click on a form in an iframe ( http://hackademix.net/2008/09/27/clickjacking-and-noscript/ ), but they can't read or modify content from another site. The notable hole in these policies at the moment is JSON/Javascript interception, which can be prevented by requiring pre-execution processing. ( http://directwebremoting.org/blog/joe/2007/04/04/how_to_protect_a_json_or_javascript_service.html )

Braden on October 15, 2008 5:16 AM

Do not implement users on your web app. Period.

PS: ;)

PPS: Use others' code, do not implement it by yourself (or not by yourself *alone*, you'll make mistakes, we all do, I'm the first one at being mistaked), better if it has been proofed, tested and eyeballed by thousands of different people (actually the only trustable code is the open sourced code, Bruce Schneier said it --or something similar).

maeghith on October 15, 2008 5:20 AM

I'm the first one at being mistaked Oh, the irony XD

maeghith on October 15, 2008 5:24 AM

Maybe someone can help me out; just for my own benefit.

What, specifically, would it take to fall victim to a CSRF/XSRF attack; from the perspective of a user?

I'd need to log into a particular website, like the NewYorkTimes.com where I've got an account. Once I've already authenticated with that site, and while that session is open/established; I'd need to visit another website - one that was intentionally designed (or intentionally hacked) to exploit the exact website I also had visited (NewYorkTimes.com).

Is my understanding correct?

I'm all for websites being secure - but this seems to be 'unlikely to happen' in a real-world environment. Maybe I'm being naive? As someone who really only visits reputable sites, it would take a malicious hacker compromising a major website (like Google) and then writing the code to exploit a large number of websites that are vulnerable to CSRF/XSRF attacks in the hopes of my having an active session/being logged in to, that particular website?

JustaNo0b on October 15, 2008 5:27 AM

Is my understanding correct?

yes

this seems to be 'unlikely to happen' in a real-world environment. Maybe I'm being naive?

Depends. Phishing is a huge problem, and Phishing + XSRF is a very real threat. Also, a lot of sites are allowing user generated content in comments, wikis, things like that. If you can *post a link*, other users of that site *WILL click on it*. And if that link points to an HTML page containing XSRF exploit on the site..

It's really not very hard at all to imagine many, many effective XSRF attacks.

Jeff Atwood on October 15, 2008 5:55 AM

Hoser:
- What evidence do you have that more punishment has a correlation with lower crime rates?
- The internet is global, and your laws are likely irrelevant where the attacker live.

Revenge is always too late.

Anonymous on October 15, 2008 6:03 AM

At last! A clear concise explanation about why everyone is getting their panties all wadded up over this issue!

So, if I have a YouTube login, and a login cookie on my browser, I could go to another website. That website could request a YouTube URL that uses my browser's cookie for authentication. Now it finally makes sense.

That really makes the use of cookies for authorization is really a no-no. That does eliminate a big convenience: Visiting a website without having to log in each and every time.

A quick question: How about the session value used heavily by PHP? Is that also browser based?

David W. on October 15, 2008 6:08 AM

What I don't understand is why we aren't made at the browser manufacturers. Why are browsers sending cookies when a logical session no longer exists? It's hard to wrap my head around this concept so here is an example.

1) I go to ING.com through my browser. The browser caches a cookie.
2) I surf for 10 minutes to some random sites.
3) I go to some nasty site that contains a CSRF for ING.com and my browser sends the cookie from #1 above simply because it is the same domain.

Why? Surely my browser is smart enough to sandbox these things. It knows that I went to site #3 separately. Maybe in some rare cases the forger may find a way to get his site into the browsing pattern so that it isn't quite as clear. But 99% of the time the browser knows thta you've visited a separate page. Why would it send a cookie from a completely unrelated browsing session?

Again, this seems like it COULD be solved by browsers and the laxed cookie handling that browsers use is mainly to blame. Or am I missing something? Again, I'm not talking about a 100% solution. But we could probably make browsers even slightly smarter and nip this thing in the bud immediately.

Matt on October 15, 2008 6:38 AM

Matt, the problem isn't strictly the browser. Imagine that ING Direct allows you to comment on a stock (say in a forum). If I can do that and the site allows me to post HTML of some kind (say to include formatting or links) then you might run afoul of cross-site scripting. It doesn't matter if you click on my link or not, but if the site isn't coded to prevent all the various forms of XSRF attacks just visiting it can cause my script to run and take some action on your behalf.

Even if it prevents that, I might post a link that you think you want to click on (like a CNN article maybe), but of course goes to my page first in order do something nefarious before passing you along to the purported destination.

It's an insidious problem and not one the browsers can easily solve. Rich internet applications make extensive use of cookies...

scottsh on October 15, 2008 6:56 AM

Great post, one point though, I've seen a fair number of people over-react to CSRF and implement a solution that breaks the back button or using the site from multiple tabs.

You mention that you:

to generate (and track, with timeout) a unique random key for every single HTML FORM you send down to the client.

I think it would help to explain how you verify that the token is correct. The naive solution where the most recently generated token is the only valid token breaks the back button (since it has a form with a previous token) and also tabbed browsing.

Also, what does generating a fresh token with each request gain for you? If you suspect an attacker can grab the token when they see the request why would the attacker not just grab your cookie so they can take complete controller over your session.

Understanding how the addition token add secure is important since developers are currently rolling their own solution.

Twitter for many months had their own solution that broke the back button and tabbed browsing. They claimed changing the only valid token with each request made the system more secure, but in reality it broke the browsing experience with no added security.

Jesse Andrews on October 15, 2008 7:04 AM

justan0ob: #1 way to prevent this sort of thing if you use sites that fall victim to it is to LOG OUT of websites you identify yourself on when you're done with them. If you close a tab, the cookie/session will hang around. If you log out, the site should refuse to acknowledge your CSRF request.

dnm on October 15, 2008 7:27 AM

@David: Using cookies for authorization is a necessity. The only other solution is using the IP address, and (other than a hole lot of other problems) would still have the same problem. Maybe you are thinking about persistent cookies, where you are still logged in after restarting the browser. But even if you don't want that, if you want to save state between one page and the next you need cookies, because it's the only way the server can identify you.

And to answer your other question, the PHP session is identified by a cookie.

Andres on October 15, 2008 7:49 AM

Jeff, why did you choose to have a unique form variable with a timeout? I can't see any advantage, but I can see disadvantages (using a form after lunch, the next day, having returned to work on Monday).

Matt (October 15, 2008 05:38 PM): because the cookie isn't the problem. If I had a link to Google here: http://www.google.com/ and you clicked it, it should return your Google page. The one with ..@gmail.com in the corner etc. In order to do that, cookies are needed. If you have gmail as your homepage, you need to send the cookie to have you logged in immediately. The same goes for the query string, or form posts. They aren't the problem, they make the web more flexible, the assumptions the web app is making are a problem.

Different Matt on October 15, 2008 9:21 AM

@Andres: a cookies for auth a necessity? What about HTTP auth? That's what it was made for.

Nicolas on October 15, 2008 9:23 AM

Just curious, but hasn't Asp.Net been protecting us against this type of attack since 1.1? I was under the impression that there was a unique key embedded into the ViewState that is checked upon every single post.

Josh on October 15, 2008 9:40 AM

This XSRF theoretical attack is like having an engine off a 747 land on your head. Possible, but I'm not staying indoors for the rest of my life because of it. Only 2 actual XSRF exploits that have ever been documented that I could find,,,yes only 2. (Google for yourself)

A vulnerability in GMail was discovered in January 2007 which allowed a attacker to steal a GMail user's contact list Great,,,,some teens list of other teens addresses that they e-mail all day long with nothing to say,,,I can get all the e-mail addresses I want in 2 seconds,,,it's called www.google.com . A different issue was discovered in Netflix which allowed an attacker to change the name and address on the account, as well as add movies to the rental queue etc... Ooooo,,,I bet this hacker wasn't hard to track down

Both these could have been accomplished with a simple keystroke logger. Hackers,,,like water,,,take the easiest path.

Show me this being used to actually get something useful, where an easier hack cannot be used,,, and I'll gladly eat my words.

GTFOH2008 on October 15, 2008 9:51 AM

GTFOH2008,

I've seen it happen to friends at 4 different startups in the valley over the last couple years. Most people don't share when they get attacked as they are afraid that it will only attract more attempts.

Jesse Andrews on October 15, 2008 12:22 PM

@Dave - there is nothing you can do to prevent DOS attacks. I suppose you can make yourself more vulnerable or easier to DOS, but a determined DOS is unstoppable (heh, except at the switch!)

scottsh on October 16, 2008 3:32 AM

Another very interesting post. At least to someone like me, with little experience of security issues.

One question that keeps occurring to me, whenever this type of attack is mentioned, is what this means for OpenID?

Does using OpenID reduce the risk, or make it worse? Or does it make no difference?

Steve on October 16, 2008 4:09 AM

Hmmmm.. security advice from someone that made the eminently hackable stackoverflow, and also relies on the notoriously insecure OpenID. Well, I guess you're trying. When are you going to change that captcha?

Gonzo on October 16, 2008 4:27 AM

Just make sure that http referer is your own site.

yp on October 16, 2008 4:44 AM

@Nicolas: Sorry, I forgot about HTTP auth, but it suffers from the same problem.

Andres on October 16, 2008 4:56 AM

@Aaron Bassett:

Where I come from, yes. It's nearly unheard of to lock your front door (a locked door is usually a sign of foreign visitors), and not uncommon to leave the keys in the ignition 24/7 (and even those who leave the keys inside of their unlocked house nevertheless leave the car doors unlocked).

It took moving to a large city for me to understand this thing about locking your doors, and by extension it opened me up to realizing how horrifically irresponsible it is to be unlocked on the sprawling digital metropolis of the Internet. Excuse the grandiose metaphor.

On the other hand, it's horrifically easy to break into a locked house. I've done it by accident when I was 15 -- my cousin was pushing the door closed as I tried to open it, and eventually succeeded in locking it without me noticing. One shove, without really using a lot of force behind it, and the door ripped off its hinges and fell into the house. In the middle of a cold winter.

We horrifyedly tried to cover it up with large amounts of wood glue, to no avail.

Ens on October 16, 2008 5:21 AM

The solution by Zeller and Felten can be improved by including a second hidden field in the form specifying the name of of a unique cookie where the cookie contains the value also included on the webform and the webform value is unique for every request, even the same user.

This will allow the form to be used multiple times in different tabs of the same browser, subject only to expiry time of the cookie for a particular instance of the form.

This will support back buttons and minimize the exposure.

John Davidson on October 16, 2008 6:18 AM

The fact that there's jail time involved in break enter lets me sleep far more soundly than the shiny new locks and bulletproof doors I just installed in my home.

In that case, you know nothing about crime.

Paul D. Waite on October 16, 2008 6:42 AM

A lot of people still seem to be missing how CSRF works.

It's not about gaining access to information, it's about tricking you, or your browser, to access URLs that perform actions.

Let's say I'm a member of the cheese of the month club, at omgcheez.com. I'm logged into that site so I have some session cookie that is only readable by omgcheez.com. Let's say the site is designed such that most activities require being logged in but use simple URLs (e.g. deactivating your membership would require nothing more than navigating to the url: http://omgcheez.com/membership/deactivate).

Now, let's say Jeff hates cheese, and he wants me to drop my cheese of the month subscription. All he has to do is trick me or my browser into accessing the http://omgcheez.com/membership/deactivate URL. And there are many ways to do that. He could put a link on some page or email and try to dupe me into clicking on it. Even easier, he could get me to go to some page of his which does nothing more than redirect to the deactivation URL. Easier yet, he could get me to go to a page containing a broken image link to that URL. Or, let's say Jeff and I both participate in the same online forum somewhere which allows posting inline images, Jeff could post a reply to some thread I read which would contain the subscription deactivation URL as an image link, boom I'm unsubscribed as soon as my browser tries to retrieve the image.

When retrieving the URL my browser will *automatically* send my session cookie to the omgcheez.com site, which will detect that I'm logged in, and it will deactivate my subscription. The fact that the referrer is some other site is irrelevant to the browser.

Note that at no time would Jeff ever have or need access to my account or session details, and yet he can got me to perform an action on my account without my consent.

The key here is sites which use predictable URLs (and inputs) to perform actions. If malicious parties are able to predict those URLs then they can get you to access them and perform actions.

Robin Goodfellow on October 16, 2008 6:46 AM


I'm waiting for a CSRF flaw on an ADSL (or similar) router to lead to DNS hijacking and the like.

Just think, at least in the UK, there are only a few predominant ISPs (sky, talktalk, bt) who have significant market share ... they all provide ADSL modems, and nearly all ADSL modems trust requests from 192.168.x.x; how long before someone manages to change the primary/secondary dns servers the ADSL router uses through a GET request?

David.

DAvid Goodwin on October 16, 2008 8:28 AM

@Hoser:

What protects you is not jail time; it's the small number of thieves compared to the large population. If your argument were true, there would be NO theft or murder in your country.

Low probability and easier pickings are what protect most people from theft.

Marcel Popescu on October 16, 2008 8:41 AM

@Brandon

I'm talking more about a root problem in the distinction between webpage, image, js, data requests. I'm specifically talking about just GET requests where no action is performed, but work is. My focus is on reducing invalid and bogus requests and if there was some verification as to what type of request it was. The browser knows what type of thing it's looking for in an image tag request, but it doesn't tell the server that it's requesting an img. That is the ROOT of this problem.

David on October 16, 2008 8:55 AM

An even stronger, albeit more complex, prevention method is to leverage server state -- to generate (and track, with timeout) a unique random key for every single HTML FORM you send down to the client. We use a variant of this method on Stack Overflow with great success.

Doesn't maintaining this kind of state open up your website to Denial-of-Service attacks? An attacker could cause your site to generate so many of these form tracking records that either 1) your server falls over from the memory required (if there's no upper limit on the total number), or 2) cause the form tracking records of legitimate users to overflow out of the table (if there is an upper limit on the total number).

Dave on October 16, 2008 9:17 AM

Do web application firewalls help?

I, too, feel this stuff is getting way to complicate for us casual web developers. Plus, this is a completely different concern in its own and it would make total sense to put a general solution in place.

Anti-CSRF/XSRF IIS7 module anyone?

Florian on October 16, 2008 9:22 AM

I agree with the remark about using HTTP authentication, we shouldn't have to roll our own, but with an addendum.

Browser support is really sucky. If I make one mistake I have to quit and restart my browser to clear out the erroneous password.

Plus, the protocol really needs a 'log out' command. Otherwise you're back where you were with cookies.

The nonce is absolutely the way to go. Protects against replay attacks/errors too.

Dylan on October 16, 2008 9:33 AM

@Matt on October 15, 2008 05:38 PM:

Why are browsers sending cookies when a logical session no longer exists?

For one thing, HTTP is a stateless or sessionless protocol. There may be some notion of a logical session in the web app, but you can only infer when to end it (i.e. explicit user log out, close the session after a timeout, etc.). And the only way to know whether a given HTTP request is part of a session is to look at stuff like cookies or a session ID in the URL [yuck]. These are pretty basic tools, and can easily be taken advantage of by any bit of JS running in the browser. (The second big problem with web apps is that the user's browser is one big pool of web pages, i.e. it's all one authentication or security environment.)

fool on October 16, 2008 10:15 AM

I think the cookie==form field technique is only strong if you set HttpOnly when setting the cookie, right? (http://www.codinghorror.com/blog/archives/001167.html)

fool on October 16, 2008 10:18 AM

All of this is very interesting, but in general, I find myself asking, why am I surfing the internet while I'm on my bank's website? I guess I have a general distrust of allowing the unwashed masses of the internet onto my pc while I'm at an important website.

By refusing to use the general internet while on a banking/financial website, you at least give yourself absolute protection at the most critical times. It is not convenient, but it is an extra layer of defense while these XSRF attacks are still out there.

Nick N on October 16, 2008 12:04 PM

Cheese is binding. The hacker did you a favor.

GTFOH2008 on October 16, 2008 12:36 PM

You know, it's funny, because I've been using a session key for every form since a long time. The reason I'm using it is to keep track of the form status to avoid resends due to back buttons. Never thought this would protect me agains cross-site scripting...

Leonardo Herrera on October 16, 2008 12:46 PM

@GTFOH2008 JustaNo0b: This attack does not necessarily require the user to navigate to some shady website and start clicking random links.

This attack can also be launched from sites that are vulnerable to a more commonly abused exploit: the XSS (cross-site scripting). That's where an attacker injects HTML in an existing page that contains the malicious code. (for example, through a comment form just like the one on this site, except which allows some HTML formatting. You see those everywhere and many are not well-secured).

Peter Bex on October 16, 2008 1:23 PM

Not sure if you wrote this on your own, but there's a version of this as a helper and actionfilter in the MVC Preview 5 Futures on Codeplex.
http://blog.codeville.net/2008/09/01/prevent-cross-site-request-forgery-csrf-using-aspnet-mvcs-antiforgerytoken-helper/

Scott Hanselman on October 16, 2008 1:25 PM

Is it possible to hack the Same origin policy for JavaScript of a browser ? Are they bulletproof from that point ?

ECC on October 16, 2008 1:45 PM

i use something like this to make forms secure (like the hidden input with the key)

input name=formname[@md5hash@][fieldname] type=text /

to make sure all content that is submitted has a valid origin. If the Server does not recognize the form or the token is already used the request will be dismissed. Also a nice way to avoid exploitation of sloppy captcha implementations

quaki on October 16, 2008 1:56 PM

It might be worth noting that Ruby on Rails has support for this method of form/request validation built-in, without the developer having to do anything. I wonder when other web frameworks will be on par with this?

Martin Probst on October 17, 2008 3:48 AM

A quick question: with Google Crome's process-per-tab and sandboxing abilities, are users of it more immune to these kinds of attacks?

Thanks.

transcriber on October 17, 2008 5:43 AM

Never use browser tabs. Never use ctrl-N to create a new browser window. Always start a new browser window from you OS menu (ie. start).

When your finished on a site which you login always use the logout button and close the window.

Always bring and Umbrella when it looks like raining. Never eat cheese before you go to bed.

Kev D. on October 17, 2008 6:21 AM

Chrome sandbox leaks sand.

No Chrome has the same problem. Session cookies on one tab leak to the other tab.

How to try it out...
Login to some site - like a message board a php based one which always displays your username will do the trick. Click some forum message post. Copy the url. Open a new tab. Paste the url. If the page you see the same page and it has your user id on it then your in trouble. DO the same trick but this time open a NEW browser (from OS menu) instead of a new tab and you won't see the same behaviour.

Tabs in firefox/ie and chrome leak cookies to each other.
I know chrome has some patches which i've not applied - so perhaps it's changed.

Of course, I'm exploring this myself for the first time - so I research the right to be wrong and/or corrected.

Kev D. on October 17, 2008 6:38 AM

Wow...how long has this problem been going on? This is scary. How long has this sort of attack been out there? I've only been out of college for a little over a year and I don't remember them mentioning this in any of my classes.

Jeff, I want to thank you - you're a big part of my personal education from post to post :) I'll be taking some time to personally research this issue further.

Joe on October 17, 2008 7:27 AM

Joe. Your not also a plumber are you ?

Plumbers inc. on October 17, 2008 7:33 AM

A simple first step is to set your browser to clear all cookies when it exits.

That helps minimize the chance that you have random authentication cookies floating around in your browser. You can then only be exploited for websites you visited *this session*.

You still get most of the benefits of cookies, but are freed of some of the risks.

Vance on October 17, 2008 9:36 AM

In this video tutorial, Stephen Walther explains how you can easily defeat JavaScript Injection Attacks and Cross-Site Scripting Attacks in your ASP.NET MVC applications by HTML encoding your content.

http://www.asp.net/learn/mvc-videos/video-400.aspx

Probably good enough as long as you're not developing an online community web site centered around programming code samples!

Zack on October 17, 2008 11:58 AM

It's fascinating how people, in the face of security advice and a continuous preaching of thinking in security terms, still fail to grasp the very basic concepts, like:

1.) If there's a possible exploit, it _will_ be exploited in an uncountable number of ways. When the first automated tool for that exploit is written, all hope is lost. GTFOH2008, with your attitude you will always be patching security holes after your software is screwed already.

2.) Things are usually more complicated than they seem. This is especially true for XSRF, and I'm not even talking about the commenters who confuse XSRF vs. XSS or SQL injection. These are totally unrelated. Unless you completely understand the problem, you cannot possibly claim that you are safe.

3.) Even then, you're not safe until you have proven it. All those who claim woohoo, my homebrew ASP.NET on Rails uber-framework is safe since 1975, did you actually try to exploit your site(s)? Do you conduct regular tests? Do you have regression-testing in place for future maintenance work? As with cryptography, small and innocent-looking changes or mistakes can invalidate all your attempts. This is especially important for XSRF, as there is no perfect solution.

I can't emphasize this enough: There is no single perfect solution to prevent XSRF. Double submit only works with JavaScript and Cookies enabled, both of which are security problems themselves, so security aware people are either unprotected or locked out. HTTP Referrer checking works these days, but only if you disallow empty referrers, so some people are, again, locked out. Both of them rely on up-to-date browsers that don't have bugs in their security model (including all plugins, like Flash). Even the terribly cumbersome method to require the user's password every time can go wrong (Is your site designed so that a user can clearly and easily see what she is about to do? If not, you're hosed again.)

And all this is just XSRF. I'm not talking about XSS, Man-in-the-middle, social engineering, Clickjacking etc. So to anybody claiming smartly how secure he is, I'd suggest to accept the fact there will always be a new attack, always an attack vectors you can't protect against, and - most importantly - always something that you could do, but didn't do yet.

BTW, David Goodwin: This is old news. Long before the current XSRF hype, things like that already happened. I'm talking of 5 years ago. Real-world XSRF is around much longer than the term itself.

Jeff, as for your method to thwart XRSF: Isn't it dangerous to include the nonce value in your HTML? After all, one of the reasons why double submit uses Cookies and JavaScript is that the same domain protection works better for cookies than for content bodies, given browser bugs, plugins etc.

Syntax-K on October 18, 2008 8:55 AM

Another public service announcement. cool.

One thing worth being clear on: If you follow the double-submitted cookie method, and the cookie value you place within the HTML form has any kind of meaning beyond preventing CSRF, you're opening yourself to other problems.

To avoid the complications, use of those approaches:

1. Make sure the cookie used in the HTML form is NEVER used for anything else (such as, say, user authentication)
2. What you put in the form is a value derived from the cookie. an HMAC of the cookie keyed on some server secret would work great for something like this.

The later approach is sane, cheap (no server state, no new cookie) and sufficient to avoid the XSRF class of attack, and is probably my favorite.

Metal Hurlant on October 19, 2008 12:03 PM

Sure your solution prevents XSRF information modification (via POST). But how does it prevent XSRF information disclosure via GET requests?

Sure I could place the cookie random value into every GET request, but then that's not very REST.

RichB on October 20, 2008 2:20 AM

@Dave
The random in-page/form-element token solution can be low overhead, something like a hash of:
secret + year(or other time-elements) + users-ip

afaik it can be used in place of the referrer and can be saved once it's used for more control (so fewer tickets to track directly).

Jim on October 20, 2008 8:02 AM

Thank you for the post; hopefully someone is still paying attention to the comments as I have an inquiry on the topic.

It is my understanding that we can not provide additional http headers for a request done via a form element; is this correct? If so, then in a purely ajax application that does not utilize forms (instead uses a RESTful web service, for example) couldn't I rely on adding a custom header value to the XHR request and checking for that value on the server? This should remove any possibility of GET/POST from img/script tags as well as forms being submitted via javascript. The only way to add the header would be through XHR, which the client side would deny if it were not same-origin.

Is this valid logic?

Nick Williams on November 23, 2008 11:55 AM

Amen to mandatory cookies, but there are a whole lot of people out there who have been told that cookies are bad, mmmkay, and just won't budge. Their brother in law, who knows enough to get their printer unjammed, told them so.
http://stroypostroy.ru/

Olof on January 29, 2009 1:46 AM

@fool: Uups, I was in misunderstanding how this double-submitted cookie actually works. So ignore the previous reply.

Actually the specified technique only works when HttpOnly is NOT set. When it is not set, only the javascript served from same site can access the cookie, due to the same-origin policy. When HttpOnly is set, javascript cannot access the cookie.

Simo on March 9, 2009 2:51 AM

@fool I think the cookie==form field technique is only strong if you set HttpOnly when setting the cookie, right?

No, let me explain.

Let's assume we have bank that has implemented this double-submitted cookie (cookie=form) technique.

Lets say you are logged in to your bank account. Then you are tricked into site http://badguys/attack.html, which has button which triggers CSRF attack to bank's site (money transaction etc).

Now, in this case, it does not matter if HttpOnly is set on the cookie, because of javascript's same origin policy does not allow http://badguy/attack.html reading bank's cookie in any case. So the double-submitted cookie technique or similar works either way.


However, hacker might try different CSRF attack than submitting a form. Maybe hacker has found a XSS exploit on bank site that can utilized with CSRF attack. In that case, HttpOnly can protect against cookie hijacking.

Example:
Lets say that user clicks link on http://badguy/attack.html that has content like a href=http://bank/exploitable_xss_site?param=some_javascript_here. Now user clicks it, and lands on bank page. The attacker javascript is executed because of XSS hole and it sends the session cookie to attackers website. This would not be possible if HttpOnly was used.

However, this does not really protect that much. If site has XSS problem, preventing attacker from getting cookie does not prevent him having his way with the website. See the URL below.

To combat this, websites sometimes require user to login again. For example, if you want to change shipping address in amazon.com, you have to enter credit card info again. This is done to prevent still-unknown XSS attacks from ordering stuff from amazon and shipping them to attackers location.

Ref:
- Same origin policy for cookies: http://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_cookies in Browser Security Handbook
- HttpOnly not so useful: http://www.gnucitizen.org/blog/why-httponly-wont-protect-you/

Simo on March 9, 2009 12:13 PM

@Metal Hurlant What you put in the form is a value derived from the cookie. an HMAC of the [session] cookie keyed on some server secret would work great for something like this. [..] [this] is sane, cheap (no server state, no new cookie) and sufficient to avoid the XSRF class of attack, and is probably my favorite.

I'm using that technique now, I can't think why using new unique cookie with timeout would be better, as suggested in this article.

However, why use HMAC? How it is more secure than say normal SHA-1 in this case? For example, lets say that on the server, following happens:

Before sending form:
html_form_key = sha1(current_session_cookie + server_secret)


After receiving form:
html_form_key = sha1(current_session_cookie + server_secret)
if received_form_key != html_form_key: print attack!


How HMAC makes this any more secure? (I don't know about HMAC).

Simo on March 9, 2009 12:27 PM

Amen to mandatory cookies, but there are a whole lot of people out there who have been told that cookies are bad, mmmkay, and just won't budge. Their brother in law, who knows enough to get their printer unjammed, told them so.
http://protectplay.ru

Soka on May 13, 2009 1:51 PM

I recently wrote an article showing how to perform CSRF so that developers can duplicate it and learn how to defend their website. You can check it out at blog.runxc.com/post/2009/07/06/CSRF-by-Example-How-to-do-it-How-to-defend-it.aspx

runxc1 on July 9, 2009 8:20 AM

Good post. This is very useful post

annuity calculator on July 29, 2009 2:32 AM

I'm sure I'm missing something, but couldn't an attacker load the web site in question in an iframe in the evil website and then lookup the fkey with javascript? As a matter of fact, could he simply use javascript to programmatically submit the form in the iframe? There must be some type of security checks done by the browser to prevent this?

Also, things will probably get really interesting once cross-site XmlHttpRequests are implemented by more browsers (Firefox 3.1 will have it)...

Jason on February 6, 2010 10:38 PM

That's very right, not XSRF can be performed through ajax. And everything's going towards ajax, so be careful about that one.

Jason P-R (stalepretzel) on February 6, 2010 10:38 PM

For those arguing about whether cookies are good or bad, be aware that this problem is larger than just cookies. This vulnerability comes into play with any kind of authentication where

1) the continuing authentication is automatic (requiring no additional user interaction), and
2) sensitive actions have well known or predictable URLs.

For the authentication part, the two most common techniques (cookie-based and HTTP auth) are both automatic; which means that subsequent HTTP requests continue with the same authentication without requiring any HUMAN interaction. Lesser used techniques such as browser-side SSL certificates also have this property. So it's not just cookie-based logins that are vulnerable.

Note there is another technique transmitting authentication credentials, URL munging (inserting some secret credential token in all URLs), which may or may not have this automatic property depending on how carefully its used. The URL munging approach though has many challenges of its own, such as leaking credentials via offsite links with referrer headers, interference with browser history, and not being RESTful, etc.

For the second part, the URLs must be predictable or guessable. If you're building a RESTful site, this unavoidable. Of course if you dispose of REST, and bookmarking, and lots of other good webby features; you can randomize your URLs. That will be quite effective at thwarting these attacks.

As mentioned, the double cookie solution works because it effectively randomizes the URL, especially in a GET. Yes, in a POST the randomness is not technically in the URL, but effectively it is still part of the identity of the resource being accessed. But also think of methods like PUT and DELETE (which can be invoked with Ajax); those can't easily be protected using the double cookie method without some unusual effort (such as URL munging, or perhaps sending extra non-standard HTTP headers to transport the second validation code/cookie copy).

Another idea, especially for super-sensitive operations, is to use a captcha in your forms. This will effectively neutralize the automatic authentication, by forcing the user to interact with the request. It doesn't even have to be a strong captcha, just as long as its value is random.

This is a very hard problem to solve; it is easy to underestimate all the attack vector variants.

Deron Meranda on February 6, 2010 10:38 PM

@yp, Jeff covered why the tactic of defending against XSRF by checking the referrer isn't really viable in his earlier post on XSRF: http://www.codinghorror.com/blog/archives/001171.html

Jon Schneider on February 6, 2010 10:38 PM

@Hoser:

1. The internet is global, how are you going to enforce these laws?
2. I'm sure your country has laws against theft. Does this mean everyone leaves their car/house unlocked?

Aaron Bassett on February 6, 2010 10:38 PM

And ASP.NET has protected against this since its inception.

It's like SQL injection. If people would just get with the times, it wouldn't even be an issue.

If possible, stop relying on old, outmoded technologies, and be very careful with new and immature ones. If you must rely on them, you should be demanding from the developers that they build these security measures directly into the framework. Individual website developers should not have to think about this kind of nonsense.

Aaron G on February 6, 2010 10:38 PM

The comments to this entry are closed.