SSL on Android: Memorizing and Pinning
(In thanks to The Guardian Project for some assistance in assembling this material, I am blogging a portion of The Busy Coder’s Guide to Android Development Version 4.6 that covers SSL on Android. This is the third part of a four-part series. The other parts include:
Note that if you are reading this in late 2013 or beyond, the material in the book may be newer than these blog posts.)
Certificate Memorizing
If your app needs to connect to arbitrary SSL servers – perhaps ones configured by the user (e.g., email client) or are intrinsic to the app’s usage (e.g., URLs in a Web browser) – detecting man-in-the-middle attacks boils down to proper SSL certificate validation… and praying for no hacked CA certificates.
However, one way to incrementally improve security is to use certificate memorizing. With this technique, each time you see a certificate that you have not seen before, or perhaps a different certificate for a site visited previously, you ask the user to confirm that it is OK to proceed. Technically savvy users may be able to deduce whether the certificate is indeed genuine; slightly less-savvy users might simply contact the site to see if this is expected behavior. The downside is that technically unsophisticated users might be baffled by the question of whether or not they should accept the certificate and may take their confusion out on you, the developer of the app that is asking the question.
You can write your own TrustManager
that maintains a roster of recognized certificates
and takes steps for unrecognized ones. You can also try
an existing implementation of a memorizing TrustManager
.
Pinning
One way to limit the possible damage from hacked CAs is to recognize that most apps do not need to communicate with arbitrary servers. Web browsers, email clients, chat clients, and the like might need to be able to communicate with whatever server the user elects to configure. But many apps just need to communicate back to their developer’s own server, such as a native client adjunct to a regular Web app.
In this case, the app does not need to accept arbitrary SSL certificates. The developer knows the actual SSL certificate used by the developer’s server, so the developer can arrange to accept only that one certificate. Or, the developer knows the CA that they get their SSL certificates from and can only accept certificates issued by that CA, and not other CAs. This reduces security a bit, but makes it easier for you to handle SSL certificate expiration and replacement without major headaches.
This technique is referred to as “pinning”. Chrome is perhaps the most well-known implementer of pinning: when you access services like Gmail from Chrome, Google (who wrote the browser) knows the valid certificates for Google (who wrote the server) and can toss out anything that is invalid… such as the TURKTRUST fake certificate mentioned earlier in this chapter.
Nikolay Elenkov has another
blog post
outlining certificate pinning support in Android 4.2. Moxie Marlinspike has an
implementation of pinning,
with a description in a
blog post.
And, the Guardian Project has an implementation of pinning in StrongTrustManager
,
discussed in tomorrow’s post.