twimco.Let’s talk ↗︎
Menu
Field notes / Product & engineering

Assume strangers can read what you shipped in the app

Anything baked into a mobile app binary can be unpacked. API keys, hidden URLs, feature flags, and "private" endpoints are not private once the build leaves your machine.

Editorial engraving of a skylit mobile lab where an engineer holds a test phone while a QA colleague opens an evidence bag of older handsets beside a blank checklist.

Treat every iOS and Android build as readable by a motivated stranger. Keep API keys, signing secrets, and privileged URLs off the client; put them behind your servers. Before each release, run a short checklist: no secrets in the binary or plist, no debug backdoors, certificate pinning decisions documented, and a plan to rotate anything that ever slipped into a store build.

The rule

If it shipped in the app, someone else can eventually read it. Obfuscation slows people down. It does not create confidentiality. Tools that hook reverse-engineering workflows to AI assistants make that fact louder; they did not invent it. Your release process should assume a stranger can inspect strings, resources, and traffic from a store build.

How the work moves

Release gate for mobile secrets

  1. Inventory

    List every key and privileged URL the app needs.

  2. Move privileged

    Keep them on the server; app gets least privilege.

  3. Scan the archive

    Secret-scan the upload artifact, not only the repo.

  4. Strip debug

    No staging switches or god-mode in production.

  5. Rotate if exposed

    Assume copies exist; monitor after rotate.

Illustrative checklist flow; not a client screenshot or measured result.

Checklist before you submit

Work through these as a gate, not a vibe:

  1. No secrets in the client. API keys with privileged scope, partner tokens, webhook signing secrets, admin URLs, and "temporary" debug passwords do not belong in the binary, plist, manifest, or bundled config. If the app needs to call you, it authenticates as a least-privileged client to your backend.
  2. Scan the build artifacts. Run your usual secret scanner on the archive you upload, not only on the repo. Look for key-shaped strings, internal hostnames, and leftover staging URLs.
  3. Strip debug doors. Hidden gesture menus, god-mode flags, and staging environment switchers should not ship in production flavors.
  4. Decide what you pin. Certificate pinning and similar controls are product decisions with operational cost. Document whether you use them, how you rotate, and who can break the glass when a pin blocks customers.
  5. Separate flavors cleanly. Staging keys must not ride along in the production lane through a shared constants file.
  6. Plan the rotate. If anything sensitive ever shipped, treat it like keys that remain in git history: rotate, monitor, and assume copies exist.
If it shipped in the app, someone else can eventually read it.

What can stay on the device

Public configuration can live on the client: non-secret feature flags, public keys meant for local verification, map tiles tokens explicitly designed as public, and endpoint hostnames that are meant to be called. Even then, prefer fetching configuration from your backend so you can change it without waiting for store review.

User secrets (session tokens, refresh tokens) belong in platform secure storage with clear logout and revocation. They are still stealable from a compromised device—design refresh and revoke accordingly—but they are not the same mistake as shipping your master key.

How this shows up in real reviews. App Store and Play review will not catch your leaked partner key. Customers will not either, until someone scrapes it and runs up a bill—or posts it. The failure mode looks like sudden API abuse, unexpected cloud spend, or a partner call about traffic from your client id.

When you hire a mobile shop or an AI-assisted builder, put "no privileged secrets in the client" in the acceptance checklist next to crash-free launch. Ask how they scan release binaries. Our note on asking how AI touched your code pairs well here: generated scaffolding is a common place for hard-coded demo keys to linger.

Decision guide

Client vs server for sensitive values

ConsiderationSafe on client (usually)Keep on server
KeysPublic keys meant for local verify.Privileged API keys and partner tokens.
URLsPublic API hosts you expect to be called.Admin, staging, and internal tool URLs.
FlagsNon-secret UX flags.Kill switches that grant privileged behavior.
SessionsUser tokens in secure storage with revoke.Long-lived master tokens embedded in the app.
ConfigFetched remote config when possible.Hard-coded secrets in constants files.
Decision framework for mobile builds; illustrative only.

What "readable" means in practice

A motivated person can unpack an Android APK or inspect an iOS IPA they obtained, list strings, and try every URL they find. They can proxy traffic from a jailbroken or instrumented device unless you invest in pinning and still accept that pinning is not secrecy for keys. University courses teach this. Commercial services sell it. AI-assisted reverse-engineering workflows make the boring parts faster. None of that requires your source repo.

So when a contractor says "it's obfuscated," translate to "slower to read, still readable." When a demo build embeds a shared staging key "just for QA," translate to "rotate that key before any store upload, and never reuse it in production."

Third-party SDKs deserve the same checklist. Analytics, maps, payments, and chat SDKs ship their own configs. Read their docs for which tokens are public-by-design versus privileged. Mis-pasting a server secret into an SDK init line is a classic leak.

Add a CI check that fails the release job on high-entropy strings in client modules matching key patterns. It will false-positive sometimes. That is cheaper than a partner invoice for abused API calls.

Release roles and the store submission packet

Name who is allowed to press submit on App Store Connect and Play Console. That person runs the checklist, not only the engineer who cut the build. Attach the secret-scan output to the release ticket. If your mobile work is handled by a vendor, make the scan artifact a contractual deliverable beside the IPA/AAB.

When you discover a secret that already shipped, follow a boring sequence: revoke or rotate, check provider logs for abuse, ship an app update if the client must change behavior, notify partners if their keys were involved, and write a short post-incident note. Do not rely on "nobody will unpack this old version." Old versions remain on devices.

Internal TestFlight and Play internal tracks are still binaries. Treat them with the same respect if they leave your laptop. A widely shared internal build is a distribution channel.

Put this into practice

We build mobile applications with server-side authority and release gates that assume inspection. If you are unsure what already shipped in your current store build, talk to us—start with a binary review and a rotation plan, not a press statement.

Education for non-engineers helps. Product managers and founders should hear once: "store builds are unpackable." That single sentence prevents a class of requests to "just put the key in the app so we can ship Friday." Pair it with a fast path for legitimate needs—a backend endpoint that mints short-lived tokens—so the secure answer is also the convenient one.

Threat modeling need not be theatrical. A one-hour session asking "what can someone extract from the store build, and what can they do with it?" catches most mistakes. Write the answers down. Update them when you add an SDK or a new deep link.

Coordinate with backend rate limits and anomaly alerts. If a client identifier suddenly spikes from unfamiliar IP ranges, you want a page that says which key was embedded historically and when it was rotated. Inventory beats heroics.

Do not publish detailed reverse-engineering tutorials in your own docs; do publish the policy: no privileged secrets in clients, scan before submit, rotate on exposure.

If your build pipeline injects environment variables into the client bundle, audit those injectors specifically. Many leaks are not typed by humans—they are copied from a server .env into a mobile config by a convenience script.

Related work: WorkStep.

twimco.

We build alongside the people doing the work.
Bring us the problem you’re thinking about.

Related work

WorkStep ↗︎