Securing Secrets in Android: What Actually Works in Production
Source: Dev.to
Introduction
In Android apps, nothing on the client is truly secret. APKs can be decompiled, strings extracted, memory inspected, and runtime behavior hooked. Treating the app as a trusted environment is the fastest way to ship insecure software. Real security comes from architecture, not hiding.
Why long‑lived secrets must never live on the device
- Storing permanent API keys or certificates in the app makes them trivially extractable.
- If an attacker obtains a long‑lived secret, they can impersonate the app indefinitely.
- Designing for short‑lived, revocable credentials limits the impact of a breach.
Using short‑lived, scoped tokens instead of embedded keys
- Obtain tokens from a backend service using a secure authentication flow (e.g., OAuth 2.0, OpenID Connect).
- Tokens should have a limited lifetime (minutes to hours) and be scoped to the minimal required permissions.
- Refresh tokens can be exchanged for new access tokens, allowing revocation without updating the app.
Proper use of Android Keystore + AES/GCM for local encryption
- Store cryptographic keys in the Android Keystore, which isolates them from the app’s process memory.
- Use AES in GCM mode for authenticated encryption of sensitive data stored locally.
- Retrieve the key only when needed and perform encryption/decryption in a short‑lived context.
What’s deprecated (Jetpack Security Crypto) and what to use instead
- The
androidx.security:security-cryptolibrary’s older APIs are deprecated for certain use‑cases. - Switch to the newer
androidx.security:security-crypto:1.1.0-alpha03(or later) which provides improved key management and integrates with the Keystore. - Follow the migration guide to replace
EncryptedSharedPreferencesandEncryptedFileconstructors with the updated builders.
Secure transport, integrity signals, and runtime hardening
- Enforce HTTPS with TLS 1.2+ and certificate pinning to prevent man‑in‑the‑middle attacks.
- Use integrity checks (e.g., SafetyNet, Play Integrity API) to detect tampered or rooted devices.
- Apply runtime obfuscation (ProGuard/R8) and code‑splitting to raise the bar for reverse engineering.
Designing systems that survive compromise, not deny it
- Assume the client can be compromised; enforce server‑side validation for every request.
- Implement rate limiting, anomaly detection, and revocation mechanisms on the backend.
- Log security‑relevant events and monitor for suspicious activity.
Conclusion
If you’re building Android apps that handle sensitive data, payments, or authentication, these practices are essential for production‑grade security.