This is a guest post by Joseph Bonneau. Joseph Bonneau is a researcher at the University of Cambridge Computer Laboratory whose research interests include privacy and security in social networks. He originally reported these vulnerabilities on his group’s research blog, Light Blue Touchpaper.
Facebook has a spotty track record enforcing the privacy of photos posted by users and designated as private. Up until last February, Facebook’s photo security relied on users not being able to craft custom PHP queries, instead of checking login cookies with every photo request. It was only a manner of time before this was hacked in a fairly spectacular way in February 2008, exposing a few personal photos of CEO Mark Zuckerberg. The “temporary flaw” was fixed, only to be hacked again in March, and again in May. The Associated Press picked up the story, Paris Hilton’s name got involved, and Facebook was forced to re-work their photo security and prevent these PHP-style hacks.
Recently, however, I’ve been poking around Facebook a bit as part of my ongoing research on privacy into online social networks, and I found a new set of problems with Facebook’s photo security. Facebook photos have URLs like http://www.facebook.com/photo.php?pid=34947682&id=210132, which include a photo id and a user id. A year ago, clicking on this link would show you my photo because Facebook didn’t check your login cookie if you knew the photo’s URL. This was the theme of last year’s hacks. In the current implementation, though, the login cookie is always checked against the photo’s access control list, so clicking on that link won’t work unless you’re one of my friends. Facebook encourages users to send out these “public photo links,” and they are posted all over the web.
Unfortunately, Facebook doesn’t actually host all of its own images, they rely on third-party content delivery networks like Akamai and LimeLight Networks to efficiently serve photos around the globe. If you examine the the HTML for a Facebook photo page, you’ll see the image is hosted at an address like http://photos-c.ak.fbcdn.net/photos-ak-sf2p/v646/41/83/210132/n210132_34947682_4899.jpg. Note the .ak subdomain in the URL, this is a clear sign that Akamai is being used, which can of course be confirmed easily using a tool like traceroute.
This direct link will give you access to the photo from the link I posted above. It doesn’t matter if you are not one of my Facebook friends or even if you have a Facebook account, this is a simple HTTP get request with no cookies at all. The photo server doesn’t check your login cookies for two reasons. First, it’s more efficient to host photos on a special-purpose, blazingly fast photo server which doesn’t have to worry about parsing PHP parameters and verifying login cookies. This kind of thing matters when you are hosting billions of photos. Second, since the photos are hosted from the domain “fbcdn.net” instead of “facebook.com,” your browser won’t send your Facebook cookies to the photo server. This is the “same origin” policy which is fundamentally built into web browsers, and is an essential feature to protect your privacy. It’s actually being used correctly here too, in that Facebook is preventing its third party photo servers from having access to your cookies from the main site by hosting them from a separate domain.
The problem though is that now the confidentiality of a photo is reduced to its URL being difficult to guess. It sure seems to have a lot of randomness to it, but you’ll find you can remove most of that; the link http://photos-c.ak.fbcdn.net/photos-ak-sf2p/210132/n210132_34947682_4899.jpg will work just as well. You’ll also notice that now three of the four numbers present in the link are the photo ID and user ID that were listed in the original “public link” to the photo as hosted within Facebook. The only thing to guess is the final 4 digits, which effectively serve as a PIN. This is the part that Facebook flubbed, we know from the history of ATM security that four digits provide far from enough randomness to prevent brute-forcing. In fact, by simultaneously querying the many servers mirroring the same photos, it’s possible try all of the 8999 possibilities in about 2 minutes.
Things get even worse, because once you have found one photo’s PIN, it is much easier to guess the PIN for the next photo from the same album. This is because the PINs are generated using a timestamp instead of a proper random number generator, a classic security mistake which has been known about for decades. I’ve coded up a script and been able to extract dozens of restricted photos from some of the public links floating around the web. Ultimately, these problems aren’t catastrophic and will probably be fixed by Facebook within a few weeks, but we can add them to the pile of security vulnerabilities on Facebook.
There is a disturbing pattern I’ve seen across many social networks in which functionality is added before the security details have been worked out, with predictably insecure results. This approach isn’t surprising for smaller social networking sites without the revenue to spend on proper security engineers, whose services are very expensive. Facebook, on the other hand, is a multi-billion dollar company which is trying to establish itself as a mature and stable platform. Amateurish mistakes like this shouldn’t be happening.