DEV Community

Cenk KURTOĞLU
Cenk KURTOĞLU

Posted on

Finding Exposed API Keys in Shipped Apps Without Being a Jerk

You've probably seen headlines: "Millions of API keys leaked in mobile apps." The reality is less dramatic but more pervasive—hardcoded secrets ship in production apps every day, often in plain sight.

I've spent years building tooling around this problem. Here's what works for finding exposed keys and how to handle findings responsibly.

Where Secrets Actually Hide

Secrets don't just live in source code. In shipped apps, they end up in:

Mobile binaries (APK/IPA): Strings embedded in compiled code, resources, or native libraries. Tools like strings, jadx, or ghidra extract them trivially.

Frontend bundles: JavaScript bundles served to browsers often contain API keys, Firebase configs, or third-party SDK credentials. View source or check the Network tab.

Desktop apps: Electron apps ship as ASAR archives. npx asar extract app.asar out gives you the full source tree. Tauri and Wails binaries embed resources similarly.

Configuration files: google-services.json, GoogleService-Info.plist, awsconfiguration.json—these ship with apps and contain keys by design.

Build artifacts: CI/CD logs, Docker images, and package registries sometimes leak secrets from build-time injection.

Practical Discovery Workflow

1. Start with automated scanning

Don't grep manually. Use purpose-built tools:

# Mobile
mobsfscan --format json --output results.json ./app.apk
trufflehog filesystem --directory ./extracted-apk --json

# Frontend
trufflehog github --repo=https://github.com/org/repo --json
trufflehog filesystem --directory ./dist --json

# Desktop (Electron)
npx asar extract app.asar extracted
trufflehog filesystem --directory ./extracted --json
Enter fullscreen mode Exit fullscreen mode

trufflehog and gitleaks understand entropy-based detection and known key formats (AWS, Stripe, GitHub, Slack, etc.). They catch high-entropy strings that regex misses.

2. Validate before reporting

False positives waste time. For each candidate:

  • Check the key format: Does it match the provider's documented pattern? (e.g., sk_live_ for Stripe, AKIA for AWS)
  • Test if it's valid: Most providers have lightweight validation endpoints. For AWS: aws sts get-caller-identity --access-key $KEY --secret-key $SECRET. For Stripe: curl -u $KEY: https://api.stripe.com/v1/account.
  • Determine scope: Is it a publishable key (safe by design) or a secret key? Firebase config objects contain apiKey fields meant to be public—they're not secrets.

3. Assess actual impact

Not every exposed key is a crisis. Ask:

  • What permissions does this key have? (Read-only analytics vs. full database write)
  • Is it rotatable? (Most are, but some legacy integrations aren't)
  • Is there evidence of misuse? Check provider logs if accessible.
  • Can the app function without it? Some keys are for optional features.

Reporting Without Being a Jerk

The goal is remediation, not clout.

Find the right channel

  • Security.txt: Check /.well-known/security.txt or /security.txt on their domain
  • GitHub: Look for SECURITY.md in the repo
  • Platform programs: Apple, Google, Microsoft, and major SaaS providers have dedicated security@ emails or bug bounty platforms (HackerOne, Bugcrowd)
  • Last resort: security@<domain> or general contact form with "SECURITY" in subject

Write a useful report

Enter fullscreen mode Exit fullscreen mode

Key principles:

  • Redact the full key in the initial report
  • Include exact location and reproduction steps
  • State the impact factually—no hyperbole
  • Offer coordination on rotation timeline
  • No demands, no deadlines, no threats

What not to do

  • Post findings publicly before disclosure
  • Demand payment (unless through a formal bug bounty)
  • Test the key beyond basic validation (don't enumerate resources, don't write data)
  • Share the key with anyone
  • Follow up aggressively—give them 7-14 days for initial response

Prevention: The Better Fix

Finding keys is reactive. The sustainable fix is preventing them from shipping.

For mobile

  • Never put secrets in the app bundle. Use a backend proxy: app → your API → third-party API. Your backend holds the secret; the app gets a short-lived token.
  • Use platform keystores: Android Keystore, iOS Keychain. They raise the bar.
  • CI scanning: Add trufflehog or gitleaks to your release pipeline. Fail the build on secrets.

For frontend

  • Environment variables at build time only: NEXT_PUBLIC_ / VITE_ / REACT_APP_ prefixes mean "this ships to the browser." Only put publishable keys there.
  • Runtime config: Fetch sensitive config from your backend at app init, behind auth.
  • Content Security Policy: Limit where scripts can load from, reducing impact of key theft.

For desktop (Electron/Tauri/Wails)

  • Don't bundle secrets in asar/resources. Same proxy pattern as mobile.
  • Use OS credential managers: keytar (Node), keyring (Python), platform APIs directly.
  • Sign and notarize: Makes tampering harder.

For all platforms

  • Rotate regularly: Automate rotation where providers support it (AWS IAM, Stripe, GitHub).
  • Scope narrowly: Use least-privilege keys. A key that only reads analytics is less damaging than one with full write access.
  • Monitor usage: Most providers show last-used timestamps and call volumes. Anomalies = investigation.

A Note on "Public" Keys

Some keys are meant to be public:

  • Stripe publishable keys (pk_live_, pk_test_)
  • Firebase apiKey in config objects
  • Google Maps API keys (with referrer restrictions)
  • Sentry DSNs
  • Algolia search-only API keys

These aren't vulnerabilities. Don't report them. Learn each provider's key taxonomy—it saves everyone time.

The Long Game

If you do this regularly, build relationships. Security teams remember reporters who:

  • Send clear, reproducible reports
  • Respect coordination timelines
  • Don't grandstand
  • Follow up to confirm fix deployment

Those reporters get faster responses, direct contacts, and sometimes early access to new features or beta programs.

Top comments (0)