Thu 15 June 2017 By Skip
One Developer’s Bad Dream
Have you ever tried to launch an app only to see your digital assets lost to a tiny hole in your API security? Do you imagine being as wildly popular as Pokémon GO, but like Pokémon GO, worry that you’ll lose control of your application’s features and anger your user base?
In Nov’16, the guys at Fallible created an online tool to reverse engineer any android app to look for secrets. Of over 16,000 apps tested, they found roughly 2500 apps contained API keys and/or secrets. That’s bad for security.
I built the backend with Node.js/Express, went mobile first, launching an Android app initially with iOS right behind, and I tied things together with a simple RESTful API. Both front and back end implementations were completely open-sourced.
A Sputtering Launch
After launch and a bit of social promotion, things began to pick up, and that’s when the trouble began.
No API keys were stored in the open source repositories. The keys were being stolen directly from the Android APKs- my app was failing the Fallible test. I tried code obfuscation and encoding the secret, but that didn’t slow them down much at all. Every time I changed the secret meant a full upgrade of the entire installed base. This was a losing game.
A Pitched Battle
My next step was to get the 3rd party GitHub API keys out of the Android app. Instead of calling the GitHub API directly from Android, I extended my own API to proxy calls to GitHub through my own back-end servers. At least I wasn’t exposing the GitHub API keys any more.
Though a step in the right direction, that didn’t slow those pesky pythonistas down very much. My app’s API key was the one key still hidden in my app, and they easily found that. Their response was to reverse engineer my own API and use it to cause my back end server to repeatedly calculate a user’s GitHub reputation, shutting my app down just as dramatically as before.
I countered with rate limiting and by caching user reputation scores on my server. They countered by creating a bunch of new users and flooding my service with fake meetup requests.
I was stuck in a never ending game of hide and seek, and all the fake traffic was costing me real money at Amazon Web Services. This was not sustainable.
No More Secrets
Why did I have an API key? It was supposed to be a secret known only to the app so that only the app could successfully call my API, but keeping that secret on the app was problematic.
My user authentication was solid. In fact, I delegated it to an OAuth2 authorization service which returned a user authorization token. My app never saw any user credentials.
So I used a similar approach to remove my last API secret. This time I delegated authentication of the app to an app authentication service. The service challenges the app and based on its responses can attest that the app is authentic and untampered. Like the user authentication service, the app authentication returns an authorization token. The special secret which generated the token is known only to my back end server and the authentication service; it’s never exposed to the app, so there’s nothing left there to steal.
Attesting app authenticity is fast and requires no human inputs, so the lifetime of any authorization token can be quite short. If any token is somehow stolen, its value is limited.
Once I got the last secret out of the app, my service stabilized. Attempts to repackage my Android app or replay API calls all failed to attest, and my costs came back down.
That would be the end of the story except for one more mistake I made. During development, I generated a long lifetime app token for testing, and I left it in the open-source repository. That put the pythonistas right back in business. Fortunately, since the secret was not in the app anymore, I simply switched to a new secret. As soon as the secret was changed, that dangerous test token was impotent, and no updates to the installed application base were needed.
When I finally woke up from my bad dream, I reflected on what might have happened.
Rather than adding features to my app and growing my user community, I had to spend a lot of time and money fighting off malicious attacks. I couldn’t afford to stay in that fight for long.
You must consider any mobile platform to be a hostile environment. It’s not just about user authenticity, which is what most developers focus on. it’s also about the authenticity of the app. Using a 3rd party service to attest your app provides a best practice approach to keeping your mobile apps secret-free and secure.