Addressing Cross-site Request Forgery

Posted by Keith McMillan

January 29, 2008 | 1 Comment

This morning I chased a link over to Dark Reading, and found an interesting article on Cross-Site Request Forgery (CSRF). Some readers may be familiar with Cross-Site Scripting (XSS) attacks. XSS attacks occur when a script on one web page is used to steal session cookies, allowing an attacker to take over an authorized user’s session, for instance.

CSRF is a variation on the theme, where rather than trying to steal sessions or other sensitive information via XSS, the scrips in question simply make requests using the user’s completely valid session credentials. Here’s a human-readable scenario that discusses the technique.

Web browsers and the protocol that they use (HTTP, Hypertext Transfer Protocol) are stateless. This means that when I visit the front page of a popular online retailer, such as Amazon.com, then click on the “books” link, HTTP gives us Amazon.com no built-in mechanism to determine that the two requests came from the same user. It’s as though you were talking with someone by writing them a separate letter for each sentence, and without any sort of indication of who’d written any particular letter. The truth is that the folks that invented HTTP never imagined that it would be put to the types of stateful uses that it is today. Online commerce wasn’t really part of that thought process.

“But wait a minute…” I hear you say, “whizbang.com (fictitious example) does know! Because they can keep track of what’s in my shopping cart!”. So what’s going on here, how do web sites keep track of your conversation? How do they string together each of the clicks you make into a stateful conversation?

A couple of different mechanisms have evolved to do this. The most popular is the use of cookies to assign a unique session ID to each conversation. The server in question makes up a long, random string and sends it to the browser as a cookie. These session cookies are typically set to expire when you shut your browser down.

Cookies, for those unfamiliar with them, are sent from the web server to the browser, and then the browser returns them back to the server as part of each request it sends. Using session cookies to uniquely identify a conversation allows the web server to keep track of the actions you’ve performed using that same session cookie. It’s as though each letter that I send (the letters containing a sentence each, remember?) had the same secret code written in the upper-right hand corner.

There are a few other techniques around to keep track of a session other than cookies, the most popular being URL rewriting and hidden form fields, but cookies are by far the most dominant, so we won’t talk about those here.

So suppose you have an account at the Foo Bank, which is very popular because they offer free network attached storage hardware when you open an account, the modern day equivalent of the free toaster (clearly, this is a fictitious scenario, but it serves to illustrate the point). Because Foo Bank is so popular, I decide to target it. I put together a web site which includes a little javascript program. This program looks through your browser history and determines if you have logged in to Foo Bank as part of your current browser session.

You visit my web page, and my program is downloaded to your browser as part of my web page when you visit my site. The program looks through your history and determines that yes, indeed, you visited Foo Bank and then came to visit my site without shutting your browser down first.

This means that there’s a chance that the session cookie sent to you from Foo Bank is still around, and still valid. My javascript program can make requests to Foo Bank without even showing you a window (yes, they can do that, this is the foundation of AJAX technologies in fact), and determine whether you’re still considered logged in.

Lo and behold, you’re session cookie is still there, and Foo Bank hasn’t logged you out for inactivity. My javascript program can now make requests to Foo Bank using your session, doing whatever I programmed it to do (such as transfer a million dollars to my offshore bank account). I don’t even need my program to access your cookie, because the browser will send it for me automatically.

One of the particularly sinister aspects of this type of attack is that it looks so much like what the application is supposed to be doing, making it particularly difficult for Foo Bank to determine that you didn’t make the request, and for you to prove to Foo Bank that you didn’t make the request. The security term for this is non-repudiation, and CSRF compromises it in a big way.

What to Do

So what can be done about CSRF? One technique is to use “leashes” on a session, tying a session to a particular IP address, or a particular domain. This is an unsatisfying solution, because anyone who’s IP address changes frequently would be forced to re-authenticate when the address does change. Most home users are dynamically assigned IP addresses by the ISP using the DHCP protocol.

Corporate users, on the other hand, are frequently behind a firewall that performs NAT, or network address translation, making it appear that the entire corporate network is really just a few (sometimes only a single) IP address. So this leashing by IP address is unsatisfactory to both ends of the spectrum.

Another approach might be to require something like Captcha, and requiring the user to enter some form of input that allows us to determine if the request is coming from a machine or a person. This is probably a generally useful technique, although it would need to be restricted to interactions where you’re content to exclude machines as one side of the conversation. What do I mean by that?

Well, SOAP, a widely used protocol for Web Services, uses HTTP as a transport mechanism, and since both sides of that conversation are machines, we couldn’t use this technique for all HTTP requests if we want to use SOAP as well. If we restrict ourselves to securing only conversations where we expect one side to be a person, we might be okay.

It’d also be pretty annoying to users if every time they wanted to send information to a web server, they had to enter a Captcha string or the equivalent, so it would be important to use such a technique judiciously, perhaps only at review and check out.

The mechanics of any a particular technique used to address a CSRF vulnerability will therefore depend on the nature of the application you’re building, on an assessment of the risks involved, and on the customer’s tolerance for the contemplated solution. As with everything else related to computer security, it’s the same old continuum: easy to use on one end, secure on the other; you have to pick your point on the line based on an assessment of, and your tolerance of, the risks involved.


RSS feed | Trackback URI

1 Comment »

2008-01-30 12:09:35

[…] wrote earlier in the week about the possible use of something like CAPTCHA to combat Cross-site Request Forgery attacks, and […]

Name (required)
E-mail (required - never shown publicly)
Your Comment (smaller size | larger size)
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> in your comment.