When you find an API key or credential embedded in your shipped app, the uncomfortable truth is that it is already compromised, and you cannot take it back. The binary is on devices and in the store, so anyone can extract the key, and removing it from a future build does nothing about the versions already out there. The only real fix is to rotate the secret, revoke the exposed one and issue a new one, and make sure the replacement does not get re-embedded the same way. Here is why rotation is mandatory after exposure and the steps to do it without repeating the mistake.
Short answer
If a secret was shipped inside your app, it is exposed and must be rotated, because anyone can extract it from the binary and you cannot recall the versions already installed. Per OWASP MASVS, the response is to revoke the exposed key and issue a new one immediately, then move the secret off the client so the replacement is not re-exposed, assess what the key could access and check for abuse, and ship an updated app that does not embed it. Rotating the key but embedding the new one in the next build just resets the problem, so the durable fix is to keep real secrets on your backend.
What you should know
- An embedded secret is already compromised: it can be extracted from the binary.
- You cannot recall shipped versions: removing it later does not help old installs.
- Rotate immediately: revoke the exposed key and issue a new one.
- Do not re-embed the new key: move it server-side, or you repeat the problem.
- Check for abuse: assess what the key could reach and whether it was used.
Why must an exposed app key be rotated?
Because exposure is permanent once a key ships in a binary. An app distributed through the store, or sideloaded, is in users' hands, and its contents can be extracted by decompiling or inspecting it, so any secret compiled in is recoverable. Crucially, you cannot un-ship it: even after you remove the key from a new build, every previously installed version still contains it, and copies of old binaries persist, so the exposed key has to be treated as known to attackers indefinitely. That is why simply deleting the key from your source and shipping an update is not a fix, the old key remains valid and exposed. Rotation, invalidating the exposed key so it no longer works and replacing it with a new one, is the only action that actually closes the exposure.
What are the rotation steps?
Revoke, replace, relocate, and review. The table lays them out.
| Step | Action |
|---|---|
| Revoke | Invalidate the exposed key so it stops working |
| Replace | Issue a new key to take over the functionality |
| Relocate | Move the new secret to your backend, off the client |
| Update | Ship an app build that does not embed the secret |
| Review | Assess what the key could access and check for misuse |
The order matters: revoke the exposed key promptly to stop ongoing abuse, issue a replacement, and place that replacement on your server rather than back in the app. Then ship an app update that calls your backend instead of holding the secret, and review your logs and the key's scope to understand what it could have reached and whether it was abused while exposed. Where a key was over-privileged, take the chance to scope the new one down.
How do you avoid re-exposing the new key?
Keep it off the client entirely. The mistake that makes rotation pointless is embedding the new key in the next build, which simply re-exposes it the same way, so the durable fix is architectural: real secrets live on your backend, and the app authenticates to your server, which holds the key and performs the privileged operation on the app's behalf. For values that genuinely must live in the app, recognize that they cannot be secret, a public client identifier or a Supabase anon key, for example, is meant to be public, so the protection has to come from server-side rules, like Row Level Security, not from hiding the key. Scope the new key to the minimum it needs, so even if something is exposed again, the blast radius is small. The principle is that a secret you ship is not a secret, so the replacement should never be shipped.
What to watch out for
The first trap is removing the key from the source and shipping an update without revoking it, which leaves the exposed key valid for every old install; you must rotate, not just delete. The second is rotating and then embedding the new key in the next build, re-creating the exposure. The third is ignoring what the key could access, when you should review for abuse and scope the replacement down. A pre-submission scan such as PTKD.com (https://ptkd.com) reads the compiled APK, AAB, or IPA against OWASP MASVS and surfaces hardcoded secrets in your build, so you find an exposed key before, or after, it ships and know to rotate it and move it server-side. Finding it is what triggers the rotation.
What to take away
- A secret shipped inside your app is already compromised and cannot be recalled, so removing it from a future build does not fix the exposure.
- Rotate it: revoke the exposed key, issue a new one, move the new secret to your backend, ship an app update that does not embed it, and review for abuse.
- Do not re-embed the new key, and for values that must live in the app, rely on server-side rules rather than secrecy, scoping keys to the minimum.
- Use a pre-submission scan such as PTKD.com to find exposed secrets in your build so you know to rotate and relocate them.



