Protect Secrets in your app - Introduction - Part 1
Nothing shipped within an app is ever fully protected. If it is in the app it can be reverse engineered. You can do the best to make it harder and more time consuming to retrieve and extract secrets.
Many applications out there depend on features provided from SDKs, which are usually protected with a SDK Key and SDK Secret which are required during configuration.
Sadly most SDK guides stop after describing on how to initialize their SDK with static strings, and do not go into detail regarding security concerns.
Let's have a look at two minimalistic usecases. Once defining the key and secret via source and once from resources.
// source FunSdk.init("amazing-key", "super-secure-secret") // resources FunSdk.init(getString(R.string.key), getString(R.string.secret))
This sadly leaves the key and secret exposed and allows for easy extraction from the published application.
You wonder how simple it is to retrieve those?
Let's have a look. Luckily Android Studio ships with a built in APK Analyzer which makes it simple to look into a compiled app.
Dragging the APK onto Android Studio will grant you with a nice overview of the APK. Beside highlighting
- APK Size (Raw size of the APK)
- Download Size (if downloaded from Google Play store)
- Contents and their size of the APK
- dex files
As the app will most likely exceed the 64k method limit (jetpack, ...) the app will include multiple dex files.
An attacker will most likely start with the
AndroidManifest.xml to find the package name or for example the specification of a
Application class, which in many cases is the place sdks get initialized.
In this sample case:
The next step is to find the
.dex file including this class. Clicking on such a file will show its contents (You can toggle "Show defined methods and fields" to speed this up).
classes2.dex don't include it in this case, but
classes3.dex is a success.
Another great features of the APK analyzer is the possibility to check out the bytecode. Right click on the
CustomAppliation and click "Show bytecode".
And there it is already.
secret in clear text.
Similar to the source the analyzer also has a feature to look into the resources of an app. Click on the
resources.arsc for this. Let's look into
strings. This will give a great list of all strings in the app. Including our
If you have looked closely you may say now: 'Oh well that was only the debug app, the release version will be a lot harder as it has obfuscation and minification via R8.'
Nice try, but it actually got even easier, as R8 was doing a great job by combining all sources into a single
.dex file. The resources stayed the same as those won't be modified by release builds.
Android apps use Kotlin (or Java) which is compiled into Java bytecode. From the perspective of protecting the app code, and keys and secrets this makes it very easy to reverse engineer.
R8 which obfuscates the app was designed to minify the code and not to protect it. There are paid solutions out there, like DexGuard or other product offerings which will try to make it even harder to reverse engineer an app, splitting strings and putting them into different locations.
Paid products are not a solution for most of us, but there's an alternative which can still help to enhance protection of keys and secrets. Native code.
Thanks to improvements of the Android tooling and full support of native code this is rather simple to achieve.
Got thoughts, feedback, improvements, suggestions, or comments?
Let me know @mike_penz