Why APK Reverse Engineering Matters
Every Android APK you publish is a package that anyone can download and decompile. Unlike server-side code, your mobile app binary is fully accessible to attackers. Embedded API keys, hardcoded secrets, Firebase configuration, Supabase URLs, and internal endpoints can all be extracted in minutes using freely available tools.
How Attackers Decompile APKs
Step 1: Obtain the APK
APKs can be downloaded from the Google Play Store using third-party tools, or extracted from a device:
# From a connected device
adb shell pm list packages | grep yourapp
adb shell pm path com.yourcompany.yourapp
adb pull /data/app/com.yourcompany.yourapp/base.apk
Step 2: Decompile with apktool
apktool d base.apk -o decompiled_app
This produces the full resource tree, AndroidManifest.xml, smali bytecode, and all assets.
Step 3: Extract Strings and Secrets
# Search for API keys and URLs
grep -r "supabase" decompiled_app/
grep -r "firebase" decompiled_app/
grep -r "api_key\|apiKey\|API_KEY" decompiled_app/
grep -rE "sk_live_|pk_live_|sk_test_" decompiled_app/ # Stripe keys
grep -rE "eyJ[A-Za-z0-9_-]+" decompiled_app/ # JWT tokens
Step 4: Decompile to Java with jadx
For a more readable view, use jadx to convert DEX bytecode back to Java:
jadx base.apk -d decompiled_java
This produces near-original Java source code that reveals application logic, API call patterns, and authentication flows.
What Attackers Look For
- Supabase anon keys and project URLs -- Used to directly query your database
- Firebase config objects -- API keys, project IDs, app IDs
- Service role keys or admin keys -- Full database access, critical severity
- Stripe secret keys -- Can create charges, read customer data
- AI/LLM API keys -- OpenAI, Anthropic, etc., used to run up your bill
- OAuth client secrets -- Impersonate your app in OAuth flows
- Hardcoded JWTs -- May contain elevated privileges
- Internal API endpoints -- Staging servers, admin panels
Defense Strategies
1. Never Embed Service-Level Keys
The most important rule: never ship service role keys, admin keys, or secret keys in your APK. These must only exist on your server.
// BAD: In your mobile app
const supabase = createClient(SUPABASE_URL, SERVICE_ROLE_KEY);
// GOOD: In your mobile app, use only the anon key
const supabase = createClient(SUPABASE_URL, ANON_KEY);
// All sensitive operations go through your backend/Edge Functions
2. Rely on Server-Side Security
Since your anon key will be extracted, design your security model assuming the attacker has it. This means:
- Enable RLS on every table
- Write restrictive policies
- Use Edge Functions for privileged operations
- Never trust the client
3. Obfuscation (Defense in Depth)
While obfuscation is not a security boundary, it slows down attackers:
// build.gradle
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
}
R8/ProGuard renames classes and methods, making decompiled code harder to read. But it does not protect string literals.
4. Use Android Keystore for Runtime Secrets
For secrets that must exist on device (e.g., encryption keys), use the Android Keystore:
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"
)
keyGenerator.init(
KeyGenSpec.Builder("my_key_alias")
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build()
)
5. Certificate Pinning
Prevent man-in-the-middle attacks by pinning your server's certificate:
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<domain-config>
<domain includeSubdomains="true">your-project.supabase.co</domain>
<pin-set expiration="2027-01-01">
<pin digest="SHA-256">base64EncodedSHA256PinHere=</pin>
</pin-set>
</domain-config>
</network-security-config>
6. Integrity Checks
Detect if your app has been tampered with or is running in a debugging environment:
fun isDebuggable(context: Context): Boolean {
return context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0
}
fun isRunningOnEmulator(): Boolean {
return Build.FINGERPRINT.contains("generic")
|| Build.MODEL.contains("Emulator")
}
Automated APK Analysis
AuditYour.app provides automated APK analysis that decompiles your app and scans for exposed Supabase and Firebase configurations, hardcoded secrets, and insecure API patterns. Upload your APK or provide a bundle ID to get a full security report with remediation guidance.
Scan your app for this vulnerability
AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.
Run Free ScanRelated
guides
Securing API Keys in Mobile Applications
Techniques for protecting secrets in mobile binaries
guides
iOS IPA Security Analysis Guide
How to analyze iOS apps for embedded secrets and vulnerabilities
guides
Preventing LLM API Key Leaks
How to avoid leaking OpenAI, Anthropic, and other AI API keys
guides
BaaS Security Architecture Guide
Architectural patterns for securing backend-as-a-service applications