Finding Exposed API Keys in Shipped Apps Responsibly
You've probably seen headlines about API keys leaked in mobile apps or researchers finding exposed secrets in APKs. The findings are real. The impact is real. But the way many people go about finding and reporting these
You've probably seen headlines about API keys leaked in mobile apps or researchers finding exposed secrets in APKs. The findings are real. The impact is real. But the way many people go about finding and reporting these issues? Often counterproductive.
I've been on both sides β hunting for exposed credentials in shipped artifacts and receiving reports that range from helpful to hostile. The difference usually comes down to verification, context, and communication.
What "Shipped" Actually Means
Before you scan anything, clarify what you're looking at. "Shipped" covers several distinct surfaces:
Mobile binaries (APK/IPA) β These are the most common target. Built artifacts often contain API keys, Firebase configs, mapping SDK tokens, and analytics keys baked in at compile time. Tools like apktool, jadx, or ghidra make extraction trivial.
Desktop apps (Electron, Tauri, native) β Same problem, different wrapper. Electron apps ship as asar archives you can unpack with npx asar extract. Native binaries need strings analysis or decompilation.
Web bundles β Less common for server-side secrets, but frontend builds sometimes leak build-time variables (NEXT_PUBLIC_, VITE_, REACT_APP_*) that weren't meant for production.
Container images β docker save + tar -x + grep -r finds .env files, config.json, and hardcoded credentials left in layers.
Each surface requires different tooling, but the verification discipline stays the same.
The Verification Gap
Here's where most reports go wrong: finding a string that looks like a key β finding an exposed key.
I learned this the hard way analyzing a reported CSRF_SECRET exposure in a library I maintain. The reporter found a cookie named csrfSecret and an environment variable CSRF_SECRET β assumed they were the same value leaking to the client. They weren't. The cookie held a per-session token; the env var was an HMAC signing key that never left server memory. The similarity was a naming collision, not a leak.
Before you report, verify:
- Is it actually a secret? Many "keys" are public identifiers: Google Maps API keys (meant for client use), Stripe publishable keys, Firebase project IDs. Check the provider's documentation.
- Does it work? Test the key against the provider's API. A revoked or restricted key isn't an active exposure.
-
What's the scope? A key restricted to
localhostor a specific referrer/domain has different severity than an unrestricted production key. - Is it rotating? Some apps embed short-lived tokens that refresh on startup. Static analysis misses the rotation logic.
Tooling That Doesn't Waste Time
Skip the regex-only scanners. They produce noise. Use tools that understand context:
| Tool | Best For | Why |
|---|---|---|
| trufflehog | Git history, filesystems, containers | Verifies secrets against live APIs, entropy analysis |
| gitleaks | Pre-commit, CI, repo scans | Fast, configurable, low false positives |
| mobsf | Mobile binaries | Static + dynamic analysis, API key extraction with context |
| apkleaks | APKs specifically | Purpose-built, decompiles + scans |
| docker-scout / trivy | Container images | Layer-aware, SBOM generation |
Run these in a controlled environment. Don't scan production systems you don't own.
The "Without Being a Jerk" Part
Responsible disclosure isn't just a buzzword β it's the difference between a fix and a lawsuit.
Find the right channel. Check SECURITY.md, the vendor's website, or security@ email. Don't DM engineers on Twitter. Don't open public GitHub issues. Don't post to Reddit.
Lead with evidence, not alarm. A good report:
- Identifies the exact file/line/artifact
- Shows the key format (redacted:
sk_live_****) - Documents your verification steps (what you tested, what worked)
- States the impact if exploited
- Asks for a timeline, doesn't demand one
Bad report: "Your app has exposed API keys!!! Fix immediately or I go public."
Good report: "In app-release.apk v3.2.1, assets/firebase-config.json contains a Firebase API key (AIza****). The key accepts unauthenticated requests to firestore.googleapis.com from any origin. I verified read access to the users collection. Please advise on your disclosure timeline."
Respect the timeline. Most vendors need 30-90 days. Some need more for coordinated releases across platforms. If they're unresponsive after 90 days, a polite follow-up is reasonable. Public disclosure without notice burns bridges and hurts users.
What Happens After You Report
You'll typically see one of three outcomes:
- Fix + rotation β Key revoked, new key issued, app updated. Best case.
- Mitigation β Key restricted (referrer allowlist, IP allowlist, scope reduction). Common for keys that can't be rotated immediately (e.g., embedded in hardware devices).
- Won't fix / by design β The key is public (Google Maps, Stripe publishable). You learn something; they close the ticket.
If it's #3, accept it. Don't argue with the vendor about their own architecture.
A Checklist Before You Hit Send
- [ ] Verified the string is a secret, not a public identifier
- [ ] Tested the key against the provider API (authenticated request succeeds)
- [ ] Confirmed the key has excessive permissions for its context
- [ ] Located the vendor's published security contact
- [ ] Drafted report with artifact location, verification steps, impact
- [ ] Redacted the full key in your report (use
sk_live_****format) - [ ] Set a calendar reminder for 90-day follow-up
The Bigger Picture
Finding exposed keys is easy. strings app.apk | grep -E '(sk_|pk_|AIza|ghp_)' takes five seconds. Doing it responsibly β verifying, contextualizing, communicating β takes longer but actually improves security.
The CSRF_SECRET confusion I mentioned? That reporter skipped verification. They pattern-matched, assumed, and escalated. The maintainer (me) spent hours proving a negative. That time could've gone to real issues.
Be the person who verifies first.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.