Patching Privacy Leaks .:. kentbrewster.com

Over the past few months I've published articles documenting an extremely trivial weakness that allows any third-party operator to determine if its users are signed in to several well-known Web services. This is pretty clearly information that ought not to be shared; even if it's not explicitly stated in the service's terms of service, it's expected by the public.

As a result of several eye-opening conversations I've had while at SxSW this year, I've taken down all of the articles detailing live exploits. As trivial as they are, I should not have disclosed any specific vulnerabilities without warning the service operators first, and I want to strongly discourage anyone from following my example, which was the wrong thing to do.

I will, however, present an overview of the methods I used, and the lessons I learned during the process.

Principles:

  • Any file on your site, whether or not it is actually JavaScript, can be included with a SCRIPT tag by a third party.
  • If the contents of that file vary sufficiently depending on the user's cookies--which almost always contain his or her login status--a third-party site can infer information which should be private, beginning with the user's login status and potentially including much more.

Detecting Vulnerabilities in Your Service:

Before you push a new feature to your AJAX-powered site, please run through the following steps:

  • Get Firefox and Firebug, and sign in to your service.
  • Go to any page that has an AJAX-powered interaction that should only be usable if the user is signed in. This could be a message post, a profile update, a mailbox read, a social network connection update ... we're looking for anything that affects the content of the page without reloading it that should only be visible if the user is logged in.
  • Once you've found it, run it.
  • Open up Firebug, go to the Net tab, and find the interaction.
  • Open the interaction's URL in a separate browser tab.
  • Go back to the original tab and sign out of your site.
  • Open a new tab, and paste in the interaction's URL again.

Analysis

View the source code of both states, and answer these two questions:

  • Does either state contain executable JavaScript?
  • If not, when both states are loaded as SCRIPT tags, do they throw different errors?

If the answer to either question is Yes, your users' login state can be guessed by a tiny scrap of JavaScript, running only in the client.

Some Suggestions

  • Don't serve live JavaScript. It's trivially easy to exploit.
  • Make sure your error messages come down in the same format, whether or not the user is logged in. If an URL returns XML when the user is logged in but redirects to the login page when he's not, you're vulnerable, because they throw different errors when included as SCRIPT tags.
  • If you're offering an API, be sure that your endpoints throw the same errors whether or not the user is logged in. Are you offering a JSON object wrapped in a callback to authenticated users and a redirect to your login page to others? One throws an error and the other does not.
  • Don't serve up hcards or vcards to signed-in users and HTML errors to outsiders. While neither is valid JavaScript, they throw different errors when included as SCRIPT tags.
  • Test all the URLs on your site that are supposed to be POSTed and be absolutely certain they return the same error whether or not the user is logged in, in the event some clever fellow tries a GET.
  • Keep an eye on your referrer and error logs. Are your AJAX endpoints starting to throw a lot of those please-go-log-in errors? Are they being linked from sites that have no business peeking or poking at them? Somebody may have already found a hole in your site.
  • Have a clearly marked path for reporting security holes, listen, and be extremely responsive!

Please Note

This isn't meant to be a definitive list, just cautionary squawking about a very common vulnerability. Please be careful; your users are depending on you.

Comments from before Disqus:

Manny .:. 2008-10-17 14:48:01
Great writeup as usual Kent, wonderful tips.
Kent Brewster .:. 2008-10-17 14:30:37
Uh-oh. Found something very ugly this morning; strongly suggest everyone SIGN OUT of Amazon.com unless actually in the process of purchasing something.
Jim .:. 2008-07-26 05:17:59
That is true, however it works for now with Facebook!

Cheers, of course yours was much more effective hack.
Kent Brewster .:. 2008-07-25 08:51:39
That's a nice catch, but trivially easy to beat with frame-busting code in the login page. (It does not, for instance, work on Yahoo's login screen.)
Jim .:. 2008-07-25 08:09:10
Apparently another hack has been leaked to do the same thing.

http://www.thinkermade.com/blog/2008/07/how-to-tell-if-a-user-is-signed-in-to-facebook-and-other-services/
Kent Brewster .:. 2008-07-24 07:14:26
If a script returns executable JavaScript, it's available for anyone else who has access to the page's DOM. Let's say your site has a stand-alone script that sees a cookie on my browser that says I'm signed in, and returns a result like this:

mySiteLogin='bob';

Any malicious third-party operator who knows about this can ping the script by including it in the body of his page with a <SCRIPT> tag. All he has to do is set a setInterval loop to check if mySiteLogin has changed every few seconds; if it does, he has your login.

This seems like it ought to be a really obvious Bad Thing, but I've run into some very popular Web 2.0 sites that send down your login, your e-mail address, and a list of your friends' logins and e-mail addresses in this exact manner. (All of those sites corrected the issue immediately, thank goodness, but I'm sure more are out there.)
woot .:. 2008-05-28 09:06:22
Hi, Thanks for the write up. What do you mean when you say "Don't serve live JavaScript?"
pdp .:. 2008-03-10 06:26:53
Interesting, but we, the hacker community, knew about it for quite some time :) never the less, I am glad that you are mention it as increasing the users' awareness is very important. Thanks