Mobile

APK Reverse Engineering & Security Analysis

How attackers extract secrets from Android apps and how to defend

Last updated 2026-01-15

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

  1. Supabase anon keys and project URLs -- Used to directly query your database
  2. Firebase config objects -- API keys, project IDs, app IDs
  3. Service role keys or admin keys -- Full database access, critical severity
  4. Stripe secret keys -- Can create charges, read customer data
  5. AI/LLM API keys -- OpenAI, Anthropic, etc., used to run up your bill
  6. OAuth client secrets -- Impersonate your app in OAuth flows
  7. Hardcoded JWTs -- May contain elevated privileges
  8. 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 Scan