App and API Security That Tastes Good
Used properly, whitelisting is a simple and effective security tactic to minimize attack surfaces. If you’re not on the list, you don‘t get in. No exceptions. Smooth as chocolate. If it’s too easy for you to find and spoof a name on the list, use indirection to make it harder. Sticky as peanut butter. Together they taste great.
A whitelist is a list of the only entities allowed to access a particular resource. Each entity is identified by one or more identifiers, such as user id, ip address, port number, or API key. It is important that these identifiers specify unique entities and that these identifiers cannot be easily mimicked by other entities.
If the list of allowed entities is small and slowly changing, then maintaining the list is quite practical. For example, if you provide a health care mobile app which communicates with your back-end services through an API, then your authorized app whitelist consists of released and approved versions of your mobile app. Versions can be added and removed from the list as features are added and retired. API calls will only be permitted if they include an app version’s identifier which is on the whitelist.
Apps often make API calls to a variety of different services. What makes a good white list identifier?
Google Maps Android API
To use the Google Maps API, you must register your app project on the Google API Console and get a Google API key which you can add to your app. From a web page, you make a call to the map service appending YOUR_API_KEY:
It is very common to use a static API key to stand in as an app or developer identifier, but how safe is it? There are two basic types of attack, reverse engineering the app code to find the API key, and observing the API key in transit during an API call.
On a web page, finding the API key may be as simple as looking at the page source in your browser. In an Android app, Google suggests placing the key in the manifest file, but that is easy to read by decompiling the APK. Even though the Google maps API call uses HTTPS, if you can mount a Man-in-the-Middle attack, you can directly observe the API key being transmitted.
You can try to hide API keys through obfuscation or encoding, but that only slows down a determined attacker. For the Android maps API, Google recommends restricting access to the signed app’s fingerprint, but this is ultimately just a second static key to determine, more difficult, but not impossible.
David Wheeler is often quoted as saying “All problems in computer science can be solved by another level of indirection.” So if hiding static API keys is hard, why not move them off the app and behind a proxy server which can indirectly make the calls for you?
With this configuration, a client app makes an API call without the API key, and the proxy server redirects the API call, appending the appropriate API key. The key is not stored in the app, nor is it ever transmitted between client and proxy.
That’s a good start, but have we really gained anything? Although we’ve removed the API keys from the app, now anyone can call the proxy, unless, we create a new app whitelist for the proxy sever, which means a new static identifier, which we’ll call the shared app secret. Sounds like the same problem all over again.
However, most apps make API calls to multiple 3rd party services. At least the API keys for all these services can now be secured behind the proxy. This makes the security of the single app secret all the more critical, but now all the API services can be centrally managed. API keys can be updated separately from the apps, and usage can be centrally logged and rate limited. Most commercial and open-source API gateways provide best practice implementations of these features.
App Authentication Services
Let’s try one more level of indirection for that remaining shared app secret. This time, we delegate identification of the authentic app to an external service.
The app secret is known to both the API proxy and the authentication service but is never directly shared with the app itself. Instead, if authentication succeeds, the app secret is used to cryptographically sign an app authentication token which is returned to the app. The app then passes the token to the API gateway with each API call. Using the app secret, the API gateway verifies the token signature before redirecting the API call, along with the appropriate API key, to the 3rd party service.
Though the secret is static, a token needn’t be. The lifetime of the token can be very short lived by giving it an expiration time, limiting the number of times it can be used or both. This way, if a token is somehow stolen, its value is quite limited. As each token expires, an app can request a new authentication and receive a fresh token.
Further, the authentication service itself can evolve over time, improving authentication strategies and/or adding additional secondary identifiers to the token.
Attesting the app identity periodically and returning time-limited tokens are big benefits of indirection over static identifying keys.
A Practical Implementation
At CriticalBlue, we have combined whitelisting with indirection to build the Approov app authentication service for mobile apps. Using Approov, only authentic and authorized apps can call your back-end APIs.
You first drop the Approov SDK into the app to enable communication with the authentication service. Next, you register your app with the authentication service which enables strong app attestation and generates the app secret shared between the service and your back-end server or API gateway.
To see it in action, you can download and try an app demo for Android or iOS. For more detail, you can follow a Hands On Mobile API Security tutorial showing how to remove all secrets from an app using both an API proxy server and an authentication service.
Android may have its treats, but for app and API security, whitelists and indirection are their own taste sensation.
Thanks for reading! I’d really appreciate it if you recommend this post (by clicking the ❤ button) so other people can find it.