How to check your APK for leaked API keys before you publish

Updated

Anything inside your APK is readable by anyone who has a copy. An APK is a zip file. Free, widely used tools turn it back into readable resources and something close to your source code in a minute: apktool for resources and the manifest, jadx for code. Shrinking and obfuscation rename your classes and methods, but the text of a string, including a key, stays exactly as you wrote it.

So the useful question is not "is there a key in my app?" (there almost always is) but "is it one that is safe to be public?"

Which keys are safe to ship?

Some keys are designed to sit in client apps, because something else protects the data behind them:

  • Firebase API keys. Firebase's own documentation says keys "restricted to Firebase services" are "OK to include in code or checked-in config files". Your data is protected by Security Rules and App Check, not by the key. See what actually protects your Firebase data.
  • Supabase publishable (or legacy anon) keys. Supabase calls these "safe to expose" in a mobile app, on one condition: Row Level Security is enabled on every table. See whether your Supabase key is safe.
  • Payment providers' publishable keys, the ones meant for the client, as opposed to their secret keys.

"Safe to ship" still means restricted. Firebase recommends a separate, restricted key for any Google Cloud API that is not a Firebase service, rather than reusing the one in your app.

Which keys must never be in an APK?

Anything that grants access by itself, with nothing behind it checking who is asking:

  • Supabase secret or service_role keys. Supabase says: "Never put one in a browser, a shipped application, or source control." It refuses a secret key from a browser, but an Android app is not a browser, so from your APK it works, and used on its own it bypasses Row Level Security.
  • AI provider keys. Firebase says the key for the Gemini Developer API "should never be included in your code or configuration files." The same is true of any AI API key: whoever holds it runs requests on your bill.
  • Payment secret keys, cloud access keys and database passwords. These act as you, not as your user.

Where do keys hide in a build?

Rarely where you think you put them. Look in:

  • res/values/strings.xml and other resources, where configuration often ends up.
  • BuildConfig fields and .env values. Build-time variables are compiled into the app as constants. Putting a secret in .env keeps it out of git, not out of the APK.
  • assets/, including bundled JSON config files.
  • The JavaScript bundle in React Native and Expo apps (assets/index.android.bundle). Hermes compiles it to bytecode, but string literals are still in it.
  • libapp.so in Flutter apps, where Dart code is compiled ahead of time, string constants included.

How can I check my own APK?

Decode it and search it. With apktool and standard command-line tools:

apktool d app-release.apk -o decoded
grep -rE "sk-|sk_live_|sb_secret_|AKIA" decoded

Those patterns catch some well-known key formats: sk- for many AI providers, sk_live_ for Stripe live secret keys, sb_secret_ for Supabase secret keys and AKIA for AWS access key IDs. They do not catch everything, and binary files such as Flutter's libapp.so or a Hermes bundle need strings run over them first:

strings decoded/lib/arm64-v8a/libapp.so | grep -E "sk-|sb_secret_|AKIA"

Legacy Supabase keys are JWTs: long strings starting eyJ, with the role encoded inside. Paste any you find into a JWT decoder; if the payload says "role": "service_role", it is the secret one.

What should I do if I find one?

  1. Rotate it first. Every copy of the build already out there, installed on phones or saved by anyone who downloaded it, still has the old key. Removing it from the next build does not take it back.
  2. Move the call behind your server. Your app calls your backend (or a serverless function); your backend holds the secret and calls the provider. The app never sees it.
  3. Restrict what stays in the app. For keys that must be in the client, limit them to your app and to the services they are for, and protect the data behind them with the provider's access rules.

Leaked keys are one item on the pre-launch checklist. The rest is what Google checks: target API level, permissions and the Data safety form.

Sources

Next steps

Still need testers? Get 12 testers for your closed test through a free test-for-test exchange - you test another developer's app, they test yours, and every opt-in is verified.

Applying for production soon? Check your APK against the Google Play launch checklist - target API, permissions that need a declaration, your Data Safety form and leaked keys. The first check is free.

Common questions

Can someone really get API keys out of my APK?
Yes. An APK is a zip file, and free tools such as apktool and jadx turn it back into readable resources and code. Obfuscation renames classes and methods, but string values like keys stay as they are.
Is my Firebase API key a leak?
Not by itself. Firebase says keys restricted to Firebase services are OK to include in code, because Security Rules and App Check, not the key, protect your data.
I shipped a secret key. Is deleting it from the next build enough?
No. Every copy of the old build still contains it, so rotate the key first, then move the call that needs it to your server.

Apps currently in closed testing

Real developers running the test described above. Test one, and get testers for your own app back.

Closed testing

FitLifeApp is a daily habit and goal-tracking app that helps users monitor water intake, step goals, and mood entries. Please test the following: - Set a daily water goal, add water intake, and check whether the totals and remaining amount update correctly. - Set a step goal and check the progress display. - Add mood entries and review previous records. - Close and reopen the app to check whether your saved data is still available. - Use the app on different days to check daily tracking and history. - Check for confusing navigation, overlapping text, or buttons that do not work correctly. Please join the Google Group and the Google Play closed test using the same Google account. Install the app through Google Play and stay enrolled for at least 14 consecutive days, using its features during the testing period. When reporting a problem, include what happened, the steps to reproduce it, your device model, and Android version. Screenshots are welcome. Thank you for helping improve FitLifeApp!

Health & FitnessProductivityLifestyle

Open to testers · 14-day test

Closed testing

Testers must join the Google Group using the same Google account they use on their Android device's Google Play Store. Testers must open the opt-in link, tap Become a tester, and then download the build via the Play Store link provided.

News & Books

Open to testers · 14-day test

Closed testing

Hi! I’m looking for testers for Crew Bites, a Flutter app that helps groups organize food orders, track who ordered what, and calculate the final bill fairly. Please test the main flow: add people, add food orders, select a restaurant/bundle, add tax/service/tip/delivery, review the total, and save or share the result. I’d especially appreciate feedback about usability, layout, performance, and any bugs on your Android device. Thank you!

Food & Drink

Open to testers · 14-day test

May we use analytics and ad measurement? Analytics (Vercel, and Ahrefs on our public pages) count page views without cookies. Ad measurement lets Google's tag on our public pages see which visits came from our Google ads, using a Google cookie. The site works the same either way. Cookie Policy · How Google uses data ·