Anything compiled into a mobile app is delivered to every attacker who downloads it, yet apps still ship with API keys, backend passwords and client secrets inside the package. M1 Improper Credential Usage heads the OWASP Mobile Top 10 2024. OWASP rates it easy to exploit, common and easy to detect, with severe technical and business impact. This guide goes beyond the overview: it sets out what M1 covers, the design rule that removes most findings (the app holds no secrets that grant backend access), how to store the credentials a user legitimately needs, how to test and evidence it with MASVS, and where it maps to other frameworks. It is for mobile developers, mobile and API security engineers, penetration testers and AppSec leads.
Key takeaways
- The app package is public. Automated tools unpack APK, AAB and IPA files and extract strings at scale; OWASP names them as the main threat agent for M1.
- A secret in a client is not a secret. RFC 8252 says secrets shipped in native apps "should not be treated as confidential secrets"; native apps are public clients.
- Move privileged calls to your backend. Provider keys for payments, AI, messaging and cloud storage belong on servers that the app reaches with a user token, never in the app itself.
- Store tokens, not passwords. OWASP's guidance is "Do not store user credentials on the device"; keep short-lived, revocable tokens in the Android Keystore or iOS keychain.
- Restricted is not the same as safe. Some keys are meant to be public, but a public key with no API or application restrictions can silently gain power when a service is enabled on the project behind it.
What it is and who it applies to
OWASP's M1 page groups four weaknesses: hardcoded credentials in source code or configuration files; insecure credential transmission, such as sending credentials unencrypted; insecure credential storage on the device; and weak user authentication that is easy to bypass. Its three example attack scenarios are an attacker who finds hardcoded credentials in the app and uses them against backend functions, one who intercepts credentials in transit and impersonates a user, and one with physical access who extracts stored credentials from a device.
The scale is well measured. Cybernews researchers analysed about 156,080 iOS apps (versions from October 2024) and reported that 71 percent leaked at least one hardcoded secret, with more than 815,000 secrets in total and about 5.2 per app. The findings included cloud storage bucket endpoints, Firebase endpoints, Google API keys, JWT signing secrets and 19 Stripe secret keys. Not all of these values are dangerous on their own, which is exactly why M1 needs a classification step rather than a blanket "remove all strings" rule.
M1 applies to every mobile app that talks to a backend or a third-party service, including apps built with cross-platform frameworks, where configuration files and JavaScript bundles are even easier to read. It overlaps with M3 Insecure Authentication/Authorization (how the backend checks the caller), M5 Insecure Communication (credentials in transit), M9 Insecure Data Storage and M10 Insufficient Cryptography. For the full list, see the OWASP Mobile Top 10 Guide.
Structure and key requirements
The first decision for any credential found in an app is which class it belongs to. That determines whether it can stay, must be restricted or must be removed.
| Credential class | Examples | Can it ship in the app? | Required handling |
|---|---|---|---|
| Public identifiers | OAuth client ID, Firebase project and app IDs, analytics IDs | Yes | Treat as public; never rely on them for authorisation |
| Restricted client keys | Maps or Firebase API keys designed for client use | Yes, only if restricted | API restrictions to named services, application restrictions (package name and signing certificate, or iOS bundle ID), quotas, backend rules |
| Server secrets | Payment provider secret keys, AI provider keys, cloud access keys, SMTP and messaging tokens, JWT signing secrets, database passwords | Never | Move the call to your backend; hold the secret in a vault; rotate anything that has shipped |
| OAuth client secrets | Confidential client secret copied into a native app | No | Register the app as a public client and use authorisation code with PKCE (RFC 8252) |
| User credentials | Passwords, PINs, recovery codes | Never stored | Exchange at login for tokens; do not persist the password |
| User session tokens | Access and refresh tokens | Yes, protected | Short lifetime, revocable, stored in platform secure storage, bound to device where possible |
OWASP's prevention list for M1 is short: avoid hardcoded credentials, encrypt credentials in transit, do not store user credentials on the device and use secure, revocable access tokens instead, implement strong user authentication, and regularly update and rotate API keys and tokens. MASVS turns those into verifiable controls:
- MASVS-AUTH-1: the app uses secure authentication and authorisation protocols and follows the relevant best practices. MASVS notes enforcement "must be on the remote endpoint".
- MASVS-STORAGE-1: the app securely stores sensitive data, which includes tokens and any key material.
- MASVS-CRYPTO-2: the app performs key management according to industry best practices, which excludes keys embedded in code.
- MASVS-NETWORK-1: the app secures all network traffic, covering credentials in transit.
- MASVS-RESILIENCE-1 and RESILIENCE-2: platform integrity and anti-tampering checks, which raise the cost of extracting runtime secrets but never make an embedded secret safe.
Platform storage reference
| Need | Android | iOS |
|---|---|---|
| Non-exportable keys | Android Keystore: key material stays in a system process; check KeyInfo.getSecurityLevel() for TRUSTED_ENVIRONMENT or STRONGBOX | Secure Enclave keys (elliptic curve only); private key never leaves the enclave |
| Strongest hardware backing | setIsStrongBoxBacked(true) on Android 9 and later, with fallback when StrongBoxUnavailableException is thrown | Secure Enclave on supported devices |
| Require user presence | setUserAuthenticationParameters() with AUTH_BIOMETRIC_STRONG or AUTH_DEVICE_CREDENTIAL | Keychain access control flags requiring user presence or biometrics |
| Token storage | Encrypt with a Keystore-held key before writing to app storage | Keychain item with a ThisDeviceOnly accessibility class such as kSecAttrAccessibleWhenUnlockedThisDeviceOnly |
| Avoid | Plain SharedPreferences, strings.xml, BuildConfig fields, assets | Info.plist, NSUserDefaults, bundled plist or JSON files, kSecAttrAccessibleAlways |
Apple's platform security guide notes that items in the kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly class are not backed up and do not sync to iCloud Keychain, and that other "this device only" items are protected with the device UID so they are useless if restored to another device.
Identity and NHI requirements
M1 is at heart a non-human identity problem. A hardcoded key is a machine credential issued to "the app", shared by every install, impossible to attribute and impossible to revoke without breaking every user. The fix is to replace that shared machine identity with three separate ones.
- The user: authenticated with OAuth 2.0 authorisation code and PKCE through an external user-agent, as RFC 8252 requires ("Public native app clients MUST implement the Proof Key for Code Exchange"). See the OAuth 2.0 and OpenID Connect Guide.
- The app instance: attested rather than trusted on a static key. Firebase App Check (DeviceCheck or App Attest on Apple platforms, Play Integrity on Android) and the Play Integrity API let the backend check that a request comes from your genuine app on a genuine device. Google says Play Integrity should be used "alongside other signals" and not as the sole mechanism, and Firebase calls App Check complementary to authentication, not a replacement.
- The backend service: the only holder of third-party provider secrets, stored in a secrets manager, scoped per service and rotated. See the Secrets Management Guide and the API Key Management Guide.
Client keys that must ship deserve the same lifecycle discipline as any NHI: an owner, an inventory entry, restrictions, a quota and a rotation plan. The Google API Keys Exposure 2026 case shows why. Truffle Security found that Google API keys embedded publicly for Maps and Firebase gained access to Gemini endpoints when the Gemini API was enabled on the same project, and CloudSEK separately reported 32 Gemini-capable keys hardcoded in 22 of the top 10,000 Android apps. A key that was harmless when shipped became a secret later, without any app change. See the Firebase Credentials Security Guide.
AI features make this sharper. Apps that call model providers directly with an embedded key hand out spending authority to anyone who unpacks them; route those calls through your backend and see the LLMjacking Guide.
Implementation steps
- Scan current releases. Run secret scanning over source repositories, build configuration and CI variables, and separately over the built APK, AAB and IPA, including bundled JavaScript, config files and native libraries. Scanning only source misses values injected at build time.
- Classify every finding. Use the credential class table above. Record owner, service, privileges and whether the value is live.
- Revoke and rotate server secrets first. Any server secret that has shipped in a release is compromised, because old versions stay on devices and mirrors. Rotate before you refactor, then confirm the old value is rejected.
- Build backend proxies. Move payment, AI, messaging and storage calls behind your own API, authenticated with the user's token and limited per user.
- Restrict the keys that stay. Apply API restrictions and application restrictions (Android package name plus signing certificate fingerprint, iOS bundle ID), quotas and backend security rules.
- Fix user authentication. Adopt authorisation code with PKCE in the system browser or platform authentication session, register the app as a public client, and remove any stored passwords.
- Move tokens to secure storage. Use Keystore-backed encryption on Android and
ThisDeviceOnlykeychain items on iOS; keep access tokens short-lived and refresh tokens revocable. - Add attestation in monitor mode. Enable App Check or Play Integrity verdict collection, review metrics, then enforce on the most sensitive endpoints first with a tiered response (allow, allow with limits, deny).
- Gate the pipeline. Fail builds on new secret findings in source and in the packaged binary, with a reviewed allowlist for public identifiers.
Rollback: keep the old key active for a short, defined overlap only while the new backend path is validated, and force upgrade for releases that still call providers directly. Attestation enforcement should be switchable per endpoint so a provider outage does not lock out users.
Decision guidance
| Situation | Choose | Trade-off |
|---|---|---|
| Third-party call needs a secret key | Backend proxy | Extra latency and server cost, but the secret never leaves your infrastructure |
| Provider offers client-restricted keys | Restricted key plus quota | Simpler, but restrictions can be spoofed by a determined attacker, so pair with attestation and backend rules |
| App needs offline access to protected data | Keystore or Secure Enclave key gated by user authentication | Requires careful handling of biometric enrolment changes and key invalidation |
| Small team, few APIs | Serverless functions as the proxy, platform attestation service | Less to run; still needs secret storage and per-user limits |
| Large estate, many apps | Central API gateway with per-app policies and shared token service | More upfront work; gives one place to revoke and monitor |
Evidence and assessment
How to test
- Static analysis of the binary: decompile the release build and search for high-entropy strings, known key prefixes, URLs with embedded credentials and configuration files. Test against the MASVS-STORAGE, CRYPTO and AUTH controls using the OWASP MASTG; see the Mobile App Security Testing Guide.
- Dynamic analysis: on a test device, inspect app storage after login and logout for passwords and tokens, and confirm tokens are cleared on logout and revoked server-side.
- Traffic analysis: confirm credentials are only sent over TLS and that no provider secrets appear in requests from the app.
- Key validity: for each embedded key, confirm with the owning team which services it can reach and that restrictions hold when called from outside the app.
What to monitor
- Usage of each client key by origin, package or bundle ID, against expected volumes.
- Attestation verdict failure rates per endpoint and app version.
- Refresh token reuse and token use from unexpected device or network contexts.
- New secret findings in source, CI and packaged builds.
Metrics
- Server secrets in shipped builds: count across all supported releases (target zero).
- Unrestricted client keys: count of shipped keys without API and application restrictions (target zero).
- Rotation time: median hours from discovery of an exposed secret to revocation.
- Attested traffic share: percentage of sensitive backend calls carrying a valid attestation token.
- Legacy release share: percentage of active installs still on versions that embed rotated or direct-call credentials.
Mapping to other frameworks
| Framework | Reference | Relevance to M1 |
|---|---|---|
| OWASP MASVS | MASVS-AUTH-1, MASVS-STORAGE-1, MASVS-CRYPTO-2, MASVS-NETWORK-1 | Verifiable requirements behind M1 |
| IETF | RFC 8252 OAuth 2.0 for Native Apps (sections 6, 8.4, 8.5) | PKCE, public clients, no confidential secrets in apps |
| NIST SP 800-53 Rev. 5 | IA-5 Authenticator Management; IA-5(7) No Embedded Unencrypted Static Authenticators; SC-28 Protection of Information at Rest; SC-12 Cryptographic Key Establishment and Management | Credential lifecycle, the explicit control against embedded authenticators, storage and key handling |
| NIST CSF 2.0 | PR.AA-01; PR.DS-01 | Managed credentials for services; protection of data at rest |
| MITRE ATT&CK Enterprise | T1552.001 Credentials In Files | Harvesting credentials from files and packages |
| MITRE ATT&CK Mobile | T1634 Credentials from Password Store; T1409 Stored Application Data | On-device credential and data theft |
| ISO/IEC 27001:2022 Annex A | A.5.17 Authentication information; A.8.24 Use of cryptography; A.8.28 Secure coding | Handling of secrets, key management and coding practice |
| CIS Controls v8.1 | Control 16 Application Software Security | Secure development and testing of in-house software |
For the ATT&CK view of harvesting credentials from files and code, see the MITRE ATT&CK T1552 Unsecured Credentials Guide.
Common pitfalls
- Obfuscation as the fix: splitting, encoding or encrypting a key in the binary only delays extraction, because the app must reconstruct it at runtime. If the key grants backend access, it has to leave the app.
- Removing the key from the next release only: old versions remain installed and downloadable, so a secret that has shipped must be rotated, not just deleted from source.
- Unrestricted "public" keys: keys meant for the client, left without API restrictions, gain new reach when services are enabled on the project, as the Google API Keys Exposure 2026 case showed.
- Confidential OAuth clients in native apps: embedding a client secret and treating the app as a confidential client, which RFC 8252 says servers must instead treat as public.
- Passwords kept for convenience: storing the user's password to re-login silently, instead of using a revocable refresh token in secure storage.
- Attestation without authorisation: trusting a valid App Check or Play Integrity verdict as proof of who the user is, when it only speaks to the app and device.
Practitioner checklist
- Scan source, CI configuration and every packaged build (APK, AAB, IPA) for secrets on each release.
- Classify every embedded value as public identifier, restricted client key or server secret.
- Rotate every server secret that has ever shipped and confirm the old value is rejected.
- Move payment, AI, messaging and cloud storage calls behind your own authenticated backend.
- Apply API and application restrictions and quotas to every client key that remains.
- Use authorisation code with PKCE through an external user-agent and register apps as public clients.
- Never store user passwords; keep short-lived tokens in Keystore-backed or
ThisDeviceOnlykeychain storage. - Add App Check or Play Integrity attestation on sensitive endpoints, starting in monitor mode.
- Track legacy app versions and force upgrade where they still carry rotated or direct-call credentials.
Standards and references
- OWASP: M1 Improper Credential Usage
- OWASP: Mobile Top 10
- OWASP: Mobile Application Security Verification Standard
- IETF: RFC 8252 OAuth 2.0 for Native Apps
- Android Developers: Android Keystore system
- Apple: Keychain data protection
- Android Developers: Play Integrity API overview
- Firebase: App Check
Related NHI Mgmt Group resources: OWASP Mobile Top 10 Guide · Mobile App Security Guide · OWASP Mobile Top 10 M9 Insecure Data Storage Guide · Secrets Management Guide · Firebase Credentials Security Guide