Cross-Site Request Forgeries and You

As the web becomes more and more pervasive, so do web-based security vulnerabilities. I talked a little bit about the most common web vulnerability, cross-site scripting, in Protecting Your Cookies: HttpOnly. Although XSS is incredibly dangerous, it's a fairly straightforward exploit to understand. Do not allow users to insert arbitrary HTML on your site. The name of the XSS game is sanitizing user input. If you stick to a whitelist based approach -- only allow input that you know to be good, and immediately discard anything else -- then you're usually well on your way to solving any XSS problems you might have.


This is a companion discussion topic for the original blog entry at: http://www.codinghorror.com/blog/2008/09/cross-site-request-forgeries-and-you.html

One of the issues I ran into trying to use Django’s built-in CSRF protection was that it would insert tokens only on sites with form elements. This means that on pages with no forms, but lots of AJAX sending POST requests, they would all be blocked. Luckily, Javascript’s same-domain rules mean I can have a page like /auth/get-csrf-token/ that simply returns the user’s current token, download it with an AJAX GET, and then freely use it in POSTS.

convincing other users to click on it?

Your example shows auto-submitting the form via JavaScript. No user needed.

I disagree with your suggestion to whitelist inputs, in order to prevent XSS attacks. I think the better solution is to sanitize OUTPUTS.

You never know where your inputs are going to go – sometimes they’ll be sent to a database, or exported to a file, or output as HTML. Each of those destinations will require different sanitization to make the data safe. At the input stage, here’s no way to sanitize data so it will be safe for any possible destination. If you try, you’ll just end up corrupting your data.

I always stick to this rule: only encode data right before you’re about to send it to another layer, and then only encode it to make it safe for that layer. Otherwise, keep your data in its purest possible form.

Thank you for explaining another common attack.

This would imply that REST implemented sites would be very vulnerable to this type of attack.

Does traditional ASP.NET forms protect you because the ViewState Mac value is embedded in forms?

Web development is scary by default.

Interesting stuff. Never heard about it before. It will take quite a lot of extra work to make your websites XRSF proof. Thanks!

Perhaps you should spend some time in the PHP community you seem to despise so; they’ve been talking about preventing cross site attacks for YEARS (ok, ill grant it’s probably only some of the smarter ones and not the unwashed masses). See http://shiflett.org/articles/foiling-cross-site-attacks as an example

Perhaps you should spend some time in the PHP community you seem to despise so

I really think it is a gross mischaracterization to say that I despise the PHP community.

This would imply that REST implemented sites would be very vulnerable to this type of attack. Does traditional ASP.NET forms protect you because the ViewState Mac value is embedded in forms?

Yes, and yes!

Your example shows auto-submitting the form via JavaScript. No user needed.

They have to click on the URL containing that form, first. The form is not running on your site, hence cross-site… scripting vulnerability.

I would say JW/Daniel/Jeff are all right. Ha!

I was just reading More Joel On Software last night, and this topic came up in a 2005 article ( http://www.joelonsoftware.com/articles/Wrong.html ). Joel promotes the idea of using Hungarian notation to put emphasis on the kind/use of the variable. A user specified variable is $usPizza for example, and when we’ve done our safe-for-SQL encoding, $sqPizza, and when we’ve done our safe-for-html encoding $htPizza (these examples could be poor - read more to inform yourself!).

A string encoded to be HTML safe isn’t the same as a string encoded to be SQL safe, which isn’t the same as a string to be Query String safe. An approach where you encode as soon as possible, like say PHP’s addslashes-to-incoming-vars approach, only solves one problem.

And looping back to Jeff’s cross site issue, this attack was commented on (by Brett first, then a couple of others) http://www.codinghorror.com/blog/archives/001100.html - which led me to read about token passing like Jeff’s second option. If you must provide proof you are the trustworthy web application with each request, through the querystring for GET or post var with POST, I’m fine with that. The approach is also extendable for cross site uses, where your application passes out a token to another web app so it can take actions using this approach. Which you want to be careful with, blah blah blah.

sigh. web safety. you can cover every hole you want, but users will always find a way to screw up (here’s an example: http://digg.com/tech_news/Fake_popup_study_sadly_confirms_most_users_are_idiots ).

/mp

Users are the most vulnerable security flaw I can think of!

Check the referrer.

Beware, the referrer header is optional, so this won’t work for a [very] small number of users who have chosen not to send it.

iThey have to click on the URL containing that form, first./i

The bad guy could send browsers to that url via the IMG trick the post opened up with, so the human subject need not actually click on anything, just view a trusted page.

For the IMG trick, what harm is it to GET something? Assuming your website doesn’t have anything destructive happen without a POST. I don’t see how that other POST trick would get around ASP.NET default security.

Please enlighten me.

I want to vote up JW’s comment. The problem is output, not input.

More specifically, the problem is data types. We fall into the trap of treating all strings like strings. In fact, an HTML string is different from a SQL string, which is different from a Javascript string. The same data would be represented differently in each of these types. Just like the number 3 is represented by different bits when treated as an integer, a double, or a character. They are all binary, right? Wrong. They are different types, each with a unique binary code.

The string is the new byte array.

If I want to post a question on Stack Overflow about Javascript, I can just start typing scriptvar blah = ‘blah, blah’;/script. That’s valid input. When stored in the database, it should be stored using those characters. The apostrophes (sorry, single quotes) are not illegal. When shown on a web page, the user should see it in exactly that form. The less-than and greater-than (I mean, angle brackets) are perfectly OK.

The only thing I need to do with that piece of data is to convert it from the string data type to the SQL data type (escape the apostrophes) in order to generate a query. And then to convert it to the HTML data type (escape the inequalities) in order to generate instructions that cause the browser to display the data correctly.

This is not a new problem. This is exactly the same as XSS. Convert strings to the proper data types, and this vulnerability goes away.

This would imply that REST implemented sites would be
very vulnerable to this type of attack.

How would a REST implemented site be any more or less vulnerable than a non-REST implemented site? It depends entirely upon the implementation doesn’t it?

@Michael L Perry: Someone has solved this problem two years ago, using Haskell’s type system.

http://blog.moertel.com/articles/2006/10/18/a-type-based-solution-to-the-strings-problem

You can’t not escape things properly because the type system will punch you in the head if you try.