Skip to content
Narvy

API keys and secrets in an APK: what can be extracted and what to do

Where secrets end up in an Android build, which keys are really secret, how to move them out of the app and how to check a release APK before publishing it.

Narvy5 min read

Anyone can download your APK and read the strings inside it. The OWASP weakness MASWE-0004 describes the problem plainly: sensitive data embedded in the app package ships with the app, "where anyone who downloads the package can recover it." Android's own guidance on hardcoded cryptographic secrets adds that an attacker with reverse engineering tools "can retrieve a hard-coded secret very easily."

This article covers where secrets end up in a build, which keys are actually secret, and what to change before the next release.

Where secrets end up in a build

MASWE-0004 lists four ways sensitive data gets into a package:

  • Source code: a key written as a constant in a class that is compiled into the app.
  • Assets and resources: strings.xml, JSON or properties files under assets/, the manifest.
  • Libraries: configuration or code of a first-party, third-party or transitive dependency.
  • Build leftovers: staging or integration URLs, developer emails, source files or map files packaged by mistake.

The last two are easy to miss because nobody on the team wrote them on purpose. A secret in a dependency's configuration file is in your APK all the same.

Obfuscation does not hide a string

The Android documentation on R8 describes obfuscation as shortening the names of classes, fields and methods to reduce the size of the DEX file. Nothing in that description touches string values: a key stored as a string literal is still a readable string in the DEX file after R8 runs.

MASWE-0004 treats white-box cryptography, code obfuscation and RASP as a last resort. In its words, these techniques "deter but do not prevent extraction and must not replace" moving the secret out of the app.

Not every key in an APK is a secret

Some keys are designed to sit in the client. Treating them as leaked secrets wastes time; treating real secrets as harmless identifiers is the actual risk.

Firebase. The Firebase documentation on API keys says keys for Firebase services "only identify your Firebase project and app to those services" and that keys restricted to Firebase services do not need to be treated as secrets. Access is controlled by IAM, Firebase Security Rules and App Check. The same page names one exception: keys for the Gemini Developer API must never be included in code or config files.

Google Maps Platform. A Maps key used by an Android app should carry an application restriction: the package name and the SHA-1 fingerprint of the signing certificate, plus an API restriction to the APIs it calls. See API security best practices.

Everything that grants access by itself is a secret: a server-side API secret, a payment provider secret key, cloud credentials, a token for your own backend that never expires, a symmetric key used to encrypt user data. If someone holding the value can call the service as you, it must not ship in the app.

What to do with a real secret

Keep it on a server. Google Cloud's best practices for managing API keys say keys "hardcoded in the source code or stored in a repository are open to interception or theft." The app calls your server, and your server adds the credential. The Maps documentation adds two rules for that proxy: require authentication for client access, and do not let clients relay arbitrary API calls through it.

Use short-lived credentials. MASWE-0004 recommends API services with real authentication and session control, with tokens that expire after a short time (it gives one hour as an example), and points to OAuth 2.0 and AppAuth.

Generate device keys on the device. For keys the app needs locally, Android recommends the Android Keystore for credentials only the app can use, or the KeyChain API for system-wide credentials. A key generated in the Keystore never appears in the APK.

Restrict what has to stay. When a key must remain in the client, MASWE-0004 asks for the minimum permissions and restrictions, so that a copy of it is worth as little as possible.

A key that has already shipped

Removing a key from the next version does not remove it from the builds already installed. The Maps documentation says it directly: "In Android and iOS apps, keys aren't replaced until customers update their apps."

For a secret that was in a published build:

  1. Treat it as exposed. Rotate or revoke it at the provider.
  2. If old app versions still depend on it, restrict it first, then check its usage before deleting it, as the Maps documentation recommends.
  3. Move the call behind your server so the next key never reaches the client.

Google Cloud also recommends creating new keys periodically, deleting old ones, and keeping only the keys you actively use.

Check your own build before release

Run these on the release APK, not on a debug build, since build types can carry different values.

List every file in the package, then look for configuration files under assets/:

unzip -l app-release.apk
unzip -p app-release.apk assets/config.json

Print the resource string pool with aapt2 (part of the Android SDK build tools) and filter it:

aapt2 dump strings app-release.apk | grep -i -E 'key|secret|token|password'

Search the strings compiled into the DEX files:

unzip -p app-release.apk 'classes*.dex' | strings -n 8 | grep -i -E 'api[_-]?key|secret|bearer'

These searches only find what you thought to search for. Also grep for the exact prefixes of the providers you use, and for your staging hostnames.

On the source side, MASWE-0004 suggests auditing the codebase and its dependencies with a secret scanner such as gitleaks. For cryptographic keys, the MASTG test MASTG-TEST-0212 looks for keys built from a hardcoded byte array, typically passed to SecretKeySpec.

Reference

  • OWASP MASVS control: MASVS-STORAGE-1, "The app securely stores sensitive data."
  • Weakness: MASWE-0004, listed in the MAS-L1 profile, the baseline for every app.
  • CWE: CWE-798 (hard-coded credentials), CWE-321 (hard-coded cryptographic key).

Official sources

  1. OWASP MASWE-0004: Sensitive Data Hardcoded in the App Packagemas.owasp.org
  2. Android Developers: Hardcoded cryptographic secretsdeveloper.android.com
  3. Android Developers: Shrink, obfuscate, and optimize your app (R8)developer.android.com
  4. Firebase: Learn about and manage API keys for Firebasefirebase.google.com
  5. Google Maps Platform: API security best practicesdevelopers.google.com
  6. Google Cloud: Best practices for managing API keysdocs.cloud.google.com
  7. Android Developers: Android Keystore systemdeveloper.android.com
  8. Android Developers: AAPT2developer.android.com
  9. OWASP MASTG-TEST-0212: Use of Hardcoded Cryptographic Keys in Codemas.owasp.org