V5: XSRF Prevention

Published October 10, 2008
Advertisement
Looks like I just missed Gaiiden's weekly journal roundup. Oh well.

I've spent today and yesterday implementing a security measure against cross-site request forgery attacks, otherwise known as XSRF attacks. These are a slightly terrifying class of attack, not least because so few people seem to be paying attention to them; an estimated 70% of sites on the web are vulnerable to - and have done nothing to guard against - this kind of attack.

XSRF is an attack in which a malicious site causes your browser to make a request to another one, in such a way that it takes advantage of the fact that you've got some cookies or some kind of session key open with that other site.

Say you've got a banking website which allows you to conduct some transactions online. They've got a web form for sending money from your account to another one; it submits data to /actions/do_transaction?to=XXXX&amt=YYYY, where XXXX is the target account number and YYYY is the amount. When you're logged into the site, your session is maintained through the use of a cookie stored on your machine.

All that I have to do is embed a 1x1 image in my page that is sourced from '//your.bank/actions/do_transaction?to=1234&amt=1000', and if you view my page while you're logged in, then presto - you've transferred $1000 to account number 1234. Your browser sees the URI that the image is supposed to come from, and issues a request for it - sending any cookies necessary to keep the session alive. It's like 'remote controlling' a session - there's no need to ever actually steal the session cookie when you can just make the browser that already holds it do what you want to do. It's known as a "confused deputy attack

So, some protections that don't work:


  • Check the referrer: easily faked, plus some users don't send referrer headers.

  • Use POST requests instead of GET requests: while this would defeat the IMG tag approach, it's trivial to get around using &#106avascript and XmlHttpRequest.

  • SSL: At no point is the connection between you and your bank site ever actually attacked in this, so securing that connection doesn't help.

  • Encrypted cookies: Again, the cookie is never actually stolen, so encrypting it won't help.



Ultimately, there is only one possible defence: Require that the request contain some information that is not stored in cookies and that malicious sites cannot know ahead of time. When your bank presents the 'transfer money' page, it includes that information in the page itself - in the HTML, or in the &#106avascript - and submits it straight back again when you've finished filling out the form. So, if a malicious site wants to obtain that information, it can only do it while you've got the actual page open - and in theory the browser security model should prevent that.<br><br>As for the information itself, something as simple as a hash of the request URI with the session ID is enough to shut down most (if not all) attack scenarios. It's got the advantage of being easily testable - all the information you need is in the request itself.<br><br>So. What I've built over the past couple of days is a WCF extension that can test messages for the XSRF-prevention token prior to the message even reaching the service operation itself. In short, all I have to do is add a couple of attributes to my service contract:<br><br><!--STARTSCRIPT--><!--source lang="cpp"--><div class="source"><pre><br> [ServiceContract]<br> [XsrfAwareBehavior]<br> interface IDiscussionService<br> {<br> [OperationContract]<br> [WebGet(UriTemplate=<span class="cpp-literal">""</span>, BodyStyle=WebMessageBodyStyle.Bare)]<br> Stream GetDiscussionOverviewPage();<br><br> [OperationContract]<br> [WebGet(UriTemplate = <span class="cpp-literal">"activeTopics"</span>, BodyStyle = WebMessageBodyStyle.Bare)]<br> Stream GetActiveTopicsPage();<br><br> [OperationContract]<br> [WebGet(UriTemplate = <span class="cpp-literal">"activeTopics.json"</span>, BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]<br> [XsrfAwareOperation]<br> GDNet.Discussion.TopicHeader[] GetActiveTopicsJson();<br><br> [OperationContract]<br> [WebGet(UriTemplate = <span class="cpp-literal">"{id}"</span>, BodyStyle = WebMessageBodyStyle.Bare)]<br> Stream GetThread(string id);<br> }<br><br></pre></div><!--ENDSCRIPT--><br>You can see one of them at the beginning - indicating that this service contract needs to be checked for XSRF-aware operations - and then the actual operation marker on the GetActiveTopicsJson() method. XsrfAwareBehavior invokes a service contract behavior I've written, which scans the contract for methods marked as XsrfAwareOperation, and inserts my token-checker into the formatting pipeline for each one.<br><br>Actually inserting the tokens into HTML is still a bit clunky - I've got a method available to my XSLT which takes the URI for a link and returns the appropriate token. It'll do for now.<br><br>Note that this doesn't protect against script injection attacks. If somebody manages to run an unauthorized &#106avascript on a page from actually within the site, then they'll have access to the cookie containing the session ID and could quite easily hash it themselves to issue requests elsewhere. V5 is not going to be quite as permissive as V4 is when it comes to custom &#106avascript, though [wink]<div> </div>
Previous Entry Member web space issues
Next Entry V5 misc
0 likes 2 comments

Comments

benryves
Quote:Original post by superpig
[...] Use POST requests instead of GET requests: while this would defeat the IMG tag approach, it's trivial to get around using &#106avascript and XmlHttpRequest. [...]
You cannot use XmlHttpRequest across domains, though as this is enforced on the client's machine it could be compromised by a bug/security breach in their browser. I'd say any HTTP request that changes something should be done over POST, anyway. I know someone who had a whole bunch of files deleted when Google's spider followed all the "delete" links (using GET) on their page!

I agree that your method is better, though. [smile]
October 11, 2008 09:42 AM
superpig
Well, OK, maybe not XmlHttpRequest, but how about an HTML <form> and a &#106avascript which automatically submits it?
October 12, 2008 05:21 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement