An overview of the mobile and API security cat and mouse game (securely storing secrets, TLS, cert pinning, bypassing protections via decompiling apps and hooking key functionality, OAuth2, etc.), described through an example back and forth between a package delivery service company and an attacker-run website trying to exploit it.
Skip walks through several mobile and API security best practices via an example company ShipFast, a package delivery service company with a mobile app and API, and ShipRaider, an attacker-run website that aims to exploit security issues in ShipFast for their benefit.
This talk does a nice job of showing how security is a cat and mouse game between attackers and defenders, especially in mobile apps, where the defender’s code is being run on devices the attacker controls.
Client Side Security Doesn’t Exist
The tl;dr of this talk, which will be unsurprising to anyone who’s been in security more than a few months, is that client side security is for the most part impossible. If an attacker has complete control over the environment in which your code is running (e.g. mobile app running on the attacker’s device, JavaScript running in the attacker’s browser, etc.), you can try to slow them down, but they can dismantle your protections eventually, given enough time, effort, and skill.
But, it’s still instructive to see the various defenses that can be used and how they can be bypassed, so let’s get into it.
App Identity Using API Keys
First, ShipFast might include an API key in the app so that the backend can verify that the request is coming from the app they expect.
But, this API key might get leaked to GitHub accidentally, or the attacker can decompile the app and ismply extract it.
The defender can try to mitigate abuse if the API key is leaked by attempting to detect API probing, app layer DDoS attacks, data scraping, credential stuffing, and by setting quotas, spike arrests, and concurrency limits (which may vary by how expensive the call is, and can be fixed or load adaptive). Stripe has a nice blog post about the leaky bucket rate limiting pattern: Scaling your API with rate limiters.
The defender can also do some behavioral analysis of API requests to detect abuse based on sequences of calls that should be impossible in practice, for example, a driver checking in all over the city faster than it would be possible to get there.
Secure Communication: Protect secrets in transit
ShipFast may try to protect secrets in transit using TLS, but ShipRaider can install their own certificate on the device so they can man-in-the-middle the traffic.
ShipFast may then try to prevent this by using certificate pinning, where the client keeps a whitelist of trusted certificates and only accepts connections from those, but ShipRaider can simply hook the pinning check using tools like SSL-TrustKiller and Frida, and have them always return true
, all is well.
Certificate pinning does impose some upkeep costs when used, as certs must be managed, the private keys stored securely, and the certs may expire or be revoked.
Remove Secret from the Channel
ShipFast may then try to not send a secret off the device at all, but instead use the client secret to compute some HMAC signature that it sends instead which the server can verify.
However, again, the attacker can simple decompile the app (using tools like dex2jar and apktool for Android apps) and find the secret.
ShipFast may then instead try to calculate the secret at runtime so there’s not a static value that can be extracted. However, ShipRaider can simply do runtime hooking and/or repackage the app to better support debugging to read the secret after it’s been calculated.
App Hardening Approaches
There are several app hardening steps that can be taken that raise the effort required by the attacker, but are still fundamentally bypassable given enough time.
Custom Secret Computation: Split a static secret into pieces, functionally recompute secret at runtime.
Obfuscation and Anti-Tamper: Obfuscate app code and make it tamper resistant, making code comprehension harder. (still vulnerable to hooking type attacks)
White-Box Cryptography: Represent a secret by its obfuscated operations – hide a function, a piece of functionality you drop in the app, instead of a secret value (can still be hooked)
Software and Hardware Backed KeyStores: These allow performing operations without exposing keys and have nice security properties, but using hardware backed keystores correctly can be challenging.
User Authentication: It’s really about App Authorization
Finally, Skip describes the OAuth2 protocol, discusses some of the common flows (Outh2 code grant flow, refresh tokens, OAuth2 Proof of Key Code Exchange (PKCE)), and then shows a few patterns aimed at further reducing attack surface via how secrets are handled.
Note: OAuth2 is actually about authorization, not authentication.
Approaches to reducing your mobile attack surface (Click to enlarge)
In the end, Skip recommends this final architecture pattern: