In February,
Arun Thampi discovered that
Path was uploading users' address books to its servers.
The
resulting kerfuffle served users well: Many apps–with Path leading the charge–are now much more careful about
how they handle sensitive contact information and how they inform users of their intentions.
However, there's another useful reminder to draw from this episode: SSL is not a security panacea. Path's app communicated with its servers over SSL, yet third parties were able to intercept and read its traffic. How? The app wasn't pinned.
What is SSL pinning?
By default, when making an SSL connection, a client checks that the server's certificate:
- has a verifiable chain of trust back to a trusted (root) certificate
- matches the requested hostname
What it does
not do is check that it is
your certificate, the one you uploaded to your server.
When you don't know in advance to which hosts you might be connecting (e.g. in a browser), checking hostname match and chain of trust is the best you can do. In most native apps, though, you know your hosts in advance. This enables a higher level of security: You can make sure that it's
your certificate that the server has presented.
This is known as
SSL pinning. It offers extra protection against
man in the middle (MITM) attacks, perhaps perpetrated using a
compromised root certicate, or via social engineering ("Free wifi! Just add this root cert to your device!").
You don't want anyone executing such an attack on your users, ever. And there are other reasons to care:
- You might not want people voluntarily snooping on (their own) SSL traffic to/from your app. Path probably didn't. (I'm glad, though, for privacy's sake, that things worked out how they did.)
- Knowing the structure of an API makes it easier to find and exploit other security holes on the server.
- Being able to easily alter server responses make it easier to find and exploit other security holes on the client. Many developers seem to assume that SSL protects you from malicious server responses. Blatant client security holes stemming from blindly trusting the server are all too common.
What apps does this affect?
The vast majority. It is incredibly easy to decrypt and observe traffic for some of the most popular apps across all categories (finance, entertainment, social networking) that many of us use daily. We've alerted every app we looked at for which we found this issue (and gave them a few months before posting this), but there are doubtless many thousands more.
At posting time, we know of four companies that implement some form of SSL pinning in their apps: card.io, Square, The Economist, and Mint.
SSL pinning is nothing new. However, with the surge in visibility of
Aldo Cortesi's remarkable
mitmproxy ever since the Path story broke, and with the explosion of native apps, it's high time SSL pinning was
de rigueur.
How do I do implement SSL pinning?
Start by setting up
mitmproxy to reproduce the problem and experience it first-hand. You should see something like the image below. We've blurred the sensitive details, but notice that the user's credentials would otherwise be plainly visible in this app:
Once you've seen how easy it is to reproduce, you can fix it! There are lots of how-to blog posts out there on SSL pinning–now that you know what to search for, half the battle is won. As for the other half...
Challenges implementing SSL pinning
There are a few problems you'll likely encounter when getting started with SSL pinning.
First, implementation is not as easy as it could be. iOS and OS X use
fairly obscure C APIs, and getting this right in Android requires digging into the
javax.net.ssl and
org.apache.http packages. I hope that Apple and Google will make this easier soon, but in the meantime, find iOS
sample code, or
an Android example, read and understand it, and use it carefully. The fundamentals are not complex.
Second, what exactly do you pin to?
- Pinning to your exact certificate will cause problems when your certificate expires and needs re-issuance.
- Pinning to your root certificate means vendor lock-in, doesn't protect against compromised root certs, and doesn't protect against some certificate chaining attacks (cf. the iOS 4 SSL Basic Constraints vulnerability).
- Pinning to the SPKI is just about right. Alas, in iOS, this requires manually parsing ASN.1, which is neither easy nor convenient. (Furthermore, parsing code is a common locus of buffer overflows, and such a security-critical code path is the last place you want that.) Android can use keystores generated with the Java keytool utility, although doing so requires configuration of an additional keystore provider. Again, better OS support here would be most welcome.
There's no magic answer here; understand the trade-offs and make an educated choice given your circumstances and platform.
But...
To anticipate a few obvious reactions:
Can't SSL pinning be bypassed by cracking the app?
Yes, it can. That's the nature of security. That doesn't mean you shouldn't make an attacker's job harder.
Aren't there are always more security holes?
Yes, there are. But this is a fairly easy one–both to exploit and to close.
I already have too many things to worry about.
Security is not a feature. It's part of the foundation of an app. Take the time, implement this once, and use it forever.
Why are you posting publicly about a security problem?
This is neither a new problem nor a new solution. This is a well known security weakness, yet–even after the Path story–it is not visible enough that many developers bother to fix it. Given that, sunlight is the best possible approach.
(Photo courtesy of
Public Domain Photos, licensed under
CC 2.0 Attribution License)