Find the Hardcoded API Key: Secrets in a Mobile Binary

Mobile Security Level 3/4 ~5 min 2026-07-29

The challenge

This Android app ships its live payment secret straight in the source. There is more than one suspicious-looking constant here, but only one is the real production API key the server trusts. Read the networking code and type that key.

What you'll learn

  • Recognise a hardcoded credential embedded in mobile source code
  • Tell a live server-trusted secret apart from a weak local crypto key
  • Understand why anything shipped in an APK can be extracted by an attacker
  • Read a Retrofit/OkHttp auth interceptor to find what credential is sent
  • Explain why client-side secrets must move to a backend

Skills tested

Mobile source reviewSecret identificationAndroid networking analysis

Prerequisites

  • Basic Kotlin reading
  • What a Bearer token / Authorization header is
  • Rough idea of how an APK is packaged

How it works

Mobile apps are distributed as files (an APK or IPA) that live on the user's device. Anything compiled or bundled into them - string constants, keys, endpoints - can be extracted with standard tools like unzip, strings, apktool, or a decompiler. So a secret hardcoded in mobile code is not a secret; it is a published credential.

In ApiClient.kt the constant API_KEY = "sk_live_9f3a2c7d4e1b" is attached to every request as Authorization: Bearer $API_KEY through an OkHttp interceptor, then used by Retrofit to call https://api.acmepay.com. The sk_live_ prefix marks it as a live (production) secret key. Whoever pulls it from the binary can talk to the payments gateway as the merchant: create charges, read transactions, or issue refunds.

The second file is a deliberate distractor. LocalCache.kt uses an all-zero AES key in ECB mode - genuinely weak crypto - but it only scrambles a local cache and is not the credential the server authenticates. The skill being tested is telling a real, server-trusted secret apart from a local weakness. The answer is the live API key, sk_live_9f3a2c7d4e1b.

Common mistakes

  • Submitting the AES key 0000000000000000. It is weak and a real bug, but it only obfuscates an on-device cache - the server never trusts it.
  • Naming the variable (API_KEY) instead of its value. The answer is the literal secret string, not the identifier.
  • Assuming the key is safe because it is private const. Visibility modifiers do not stop extraction from the packaged binary.
  • Ignoring the sk_live_ prefix. That prefix is the tell that this is a production credential, not a test or dummy value.

How to defend against it

Never embed live API keys or other server-trusted secrets in a mobile client. Treat the app binary as fully readable by anyone who installs it.

  • Move privileged calls behind your own backend; the app authenticates the user, and the backend holds the gateway secret.
  • Issue short-lived, scoped tokens to the client instead of a long-lived master key.
  • Rotate any key that has shipped in a client immediately - assume it is already compromised.
  • Scan builds in CI for secret patterns (for example sk_live_) so they never reach a release.

Full solution

Pro and Max members unlock the complete step-by-step walkthrough.

Go Pro

Community stats

127 completions
89% success rate
M2F14M3 First blood

Related Daily Hacks

18,000+ Hackers 100+ Labs & Courses Free
Start Hacking Free