== A browser sent a request. Did the user mean to send it? == The Zen of user intent and action Covers CSRF, clickjacking and maybe a teensy bit of social engineering Outline: 1. Wait, what? - A Synopsis of CSRF 2. CSRF protection 3. CSRF protection in practice 4. Clickjacking 5. Clickjacking protections == Wait, what? == Today, we're going to look at one of the fundamental assumptions of most programmers, and then show you that it's not true. The assumption is: given a request that has the correct session cookie for a user, that request is what the user explicitly requested. Now let's debunk it. The basic method we're going to do is use some code to trick a browser into making a request when the user doesn't realize it, by doing so in the background. To do this, the user does have to visit my malicious webpage, but this is something that is not too difficult to do with a little social engineering. "DON'T CLICK HERE!" First, GET requests. These are easy. Make an tag with the src pointing to the page you want to forge a request to. A browser sees the img tag, attempts to load up the URL, gets some content that's not an image, and fails out. GET requests are supposed to be *idempotent*, that is, when one happens the world is not changed: no users are deleted, no posts are submitted, no nuclear missiles are launched. So in theory, such an image tag shouldn't actually make a difference. In practice, many applications implement their logout function as a plain text link to logout.php. You can have lots of fun with this! Next, POST requests. Maybe a little harder to do, since there's no way I can make an img tag post to another website? Wrong! Here are two ways I can forge a POST request: 1. Create a
on my page that points to your website, and fill it in with the appropriate values I want to submit as hidden inputs. Then, using JavaScript, I can submit the form automatically. If I want to be discrete about it, I'll put this form in an iframe. 2. Use XmlHttpRequest to send a POST request. Requests can be forged. == CSRF Protection == While POST requests can be forged, the attacker isn't actually able to see what the contents of the requested page are. This is due to the same-origin policy, and is crucial to CSRF protection. The usual technique is to include an extra hidden field in your forms called a "nonce" or a "token". This token is associated with the user's session and checked when the user submits the form. If the form was legitimately submitted, the token will be present and will match the one stored in the user's session; if it was forged the tokens won't match, and you can reject the request. Instead of:
You have:
== CSRF protection in practice == If you are using some sort of form generator, there will probably be a centralized location you can add the hooks necessary for adding the token variable. This is highly implementation dependent. If you are not and your form code is scattered all over the place, you may want to consider dynamically rewriting your output HTML to have the token. If you're on PHP, check out http://csrf.htmlpurifier.org If you're on Django, check out the "Cross Site Request Forgery protection" Be sure to make your AJAX know about any CSRF protections you put in place, too. Usually, including a small inline script with the appropriate token works well. Be sure NOT to put it in an external script; otherwise an attacker can just load it using the When your page is loaded in as an iframe, this code run and it busts out of the iframe. == Conclusion == I hope you've learned a bit form this introduction. Most of this information can be found on the web on various resources; the key is to keep the knowledge in mind, so that when you code your own application you realize what you need to do, and act accordingly. A little knowledge goes a long way. Happy hacking!