Communicating securely between a mobile app and a the corresponding backend is not a trivial task. Sure, nowadays we can write https and almost be certain our app is actually having encrypted communication with the right backend. However, just recently, I decided to do a communications check of apps on my iPhone using Fiddler as a proxy and was surprised to find that I could do a man-in-the-middle attack for an app I use regularly without the app noticing this. Apparently, the developers had opted to disable certificate validation (perhaps so the app would work with a developer backend that doesn’t have a pricey SSL server certificate installed) and forgot to turn it on again before publishing the app to the store.
However, there is an even better way to solve that problem that doesn’t involve buying an SSL server certificate. It’s called certificate pinning. Instead of relying on the operating system to check the validity of the presented server certificate by looking at its list of root certification authorities (which may contain entries the user is not aware of, see Superfish) developers can instruct their https calls to only accept a specific server certificate or, better yet, only certificates issued by a specific certification authority. This is not only much safer, it also avoids the costs of buying SSL server certificates.
So there we are, we have a method to ensure our app is talking to the right backend. The trickier part, though, is ensuring that whoever is making the call to our backend is actually who we think they are (i.e., our own app).
A simple approach to this problem would be to include an SSL client certificate in all requests to the backend, using a certificate whose private key is included as part of the app bundle. The problem with this is that it’s just not possible to hide the certificate well enough in our bundle to make it impossible to extract that certificate and its private key through reverse engineering. And since all clients would be using the same certificate, having that certificate compromised means we cannot tell if it’s acutally our app that is calling the backend.
The solution to this problem is to issue individual SSL client certificates to each device that is accessing the backend.
The vendors of Mobile Device Managament (MDM) software have great solutions that do exactly that. The problem is that these solutions can only be applied to devices that are in full control of the MDM solution. That’s great for internal apps on company devices but doesn’t help at all for apps you’re distributing to others.
Here is my proposal: App store operators should include a method to create an unique SSL client certificate upon installation of the app that is signed either using a certificate (public and private key) the app creator uploads into the app store or a certificate created by the app store vendor. This would make it very easy to check on the server side if a request is coming from an app that was actually distributed through the app store (and paid for, in the case of non-free apps) by using the signing certificate’s public key.
Such a feature could also be included in cloud backends like Azure Mobile Services where one could limit requests only to genuine apps and provide the corresponding functionality the client libraries accessing the backend.
All in all, this approach would greatly increase app communication security without much effort for the app developer. Now, how to convince the app store operators?