Android security checklist
Skill esaldgut/ai-native-engineering-workspace/global-skills/android/android-security-checklist
Systematic Android security checklist aligned with OWASP MASVS and platform guidance — secrets via DataStore + Google Tink (AeadSerializer) + Android Keystore (NOT the deprecated EncryptedSharedPreferences), TLS cert pinning via Network Security Config (SPKI hash) and/or OkHttp CertificatePinner with a backup pin, OAuth via Chrome Custom Tabs + PKCE + AppAuth-Android (WebView is forbidden), and manifest hardening (android:exported, allowBackup=false, usesCleartextTraffic=false). Use when handling tokens, network security, sign-in, or attack-surface review on Android.From its SKILL.md
npx -y skills add esaldgut/ai-native-engineering-workspace --skill android-security-checklistAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
10.2 KB, ~2.1k tokens by cl100k_base, as published. Nobody here has run it
Android security checklist (MASVS-aligned)
A boundary-by-boundary checklist for Android apps: secrets storage, transport security,
authentication, attack surface, and input validation — each anchored to developer.android.com or
OWASP MASVS. The headline change since pre-2024 advice:
EncryptedSharedPreferences is deprecated — any checklist still recommending it is stale.
When to invoke
- You're storing a token/secret, configuring TLS, building a sign-in flow, or reviewing
AndroidManifest.xmlattack surface. - You see
EncryptedSharedPreferences, aWebViewused for OAuth, a single TLS pin with no backup, orandroid:exported="true"without apermission— fix each per the rules below.
Announce on invoke: "Using android-security-checklist to review secrets / transport / auth /
manifest against OWASP MASVS and the current Android security guidance."
1. Secrets storage — EncryptedSharedPreferences is OUT
androidx.security:security-crypto (Jetpack Security) reached its terminal release (1.1.0) and
EncryptedSharedPreferences is deprecated — driven by Keystore inconsistency across OEMs (keyset
corruption crashes), main-thread IO StrictMode violations, and no clean encryption-scheme upgrade path.
Canonical replacement: DataStore + Google Tink + Android Keystore.
- DataStore (
androidx.datastore:datastore) is the persistence layer — but it is not encrypted by default. "Migrated to DataStore, done" is a security bug for sensitive values. - Tink does the encryption.
androidx.datastore:datastore-tinkships anAeadSerializer(Authenticated Encryption with Associated Data) that encrypts/decrypts the DataStore payload for you. - Android Keystore protects the master key (
android-keystore://…), so the key never leaves hardware- backed storage. - Migrate existing data with the
SharedPreferencesMigrationconstructor that injects the oldSharedPreferences(including a formerEncryptedSharedPreferences) into the new DataStore.
// Tink AEAD master key in the Android Keystore; encrypt before persisting via DataStore
val aead: Aead = AndroidKeysetManager.Builder()
.withSharedPref(context, "master_keyset", "master_key_prefs")
.withKeyTemplate(AeadKeyTemplates.AES256_GCM)
.withMasterKeyUri("android-keystore://my_master_key")
.build()
.keysetHandle
.getPrimitive(Aead::class.java)
val ciphertext = aead.encrypt(tokenBytes, /* associatedData */ null)
// persist `ciphertext` through DataStore (e.g. datastore-tink AeadSerializer)
For preferences (theme, last tab) DataStore alone is fine — no encryption needed. Encryption is mandatory only for sensitive values (tokens, PII).
2. Transport security — Network Security Config + (optional) OkHttp pinning
Two complementary paths (not mutually exclusive):
- Network Security Config (
res/xml/network_security_config.xml) — declarative, enforced at the OSNetworkSecurityPolicylevel, so it covers all network libraries (OkHttp,HttpsURLConnection, anything on platform sockets). Use it for the global no-cleartext baseline and per-domainpin-set. Reference it from the manifest withandroid:networkSecurityConfig. - OkHttp
CertificatePinner— programmatic, scoped to that one OkHttp client. Same SPKI hash format. Many apps use both: NSC for global policy,CertificatePinnerfor the hashes on the API client.
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors><certificates src="system"/></trust-anchors>
</base-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2027-01-01">
<pin digest="SHA-256">PRIMARY_SPKI_HASH_BASE64=</pin>
<pin digest="SHA-256">BACKUP_SPKI_HASH_BASE64=</pin>
</pin-set>
</domain-config>
</network-security-config>
- The pin is the SHA-256 of the SubjectPublicKeyInfo (SPKI), NOT the whole certificate. Generate it:
openssl x509 -pubkey -noout -in cert.pem | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64. - Always include ≥1 backup pin. A single pin makes cert rotation a total outage; the backup (next intermediate or next-rotation key) prevents it (MASVS-NETWORK-4).
3. Authentication — Custom Tabs + PKCE, never WebView
- WebView for OAuth is a Google policy violation, not just a best practice. Google explicitly rejects
embedded WebViews at the authorization endpoint (a host app can keylog the login form). Any compliant
IdP refuses the auth. Chrome Custom Tabs (
androidx.browser:browser) is the only compliant browser surface. - PKCE is mandatory for public clients (
code_verifier+code_challenge= SHA-256 of the verifier) per RFC 8252 (OAuth 2.0 for Native Apps). - AppAuth-Android is the canonical implementation — it uses Custom Tabs and PKCE and deliberately does not support WebView.
- MASVS-AUTH-2: never rely on client-side authorization. The server re-authorizes every request; UI gating is UX, not a security boundary.
4. Manifest hardening (attack-surface reduction)
<application
android:allowBackup="false"
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="false">
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
android:exportedis required from API 31. Default tofalse. Settrueonly for components with intent-filters that legitimately receive external intents; protect sensitive ones withandroid:permission.exported="true"without apermission(and without being a launcher/validated deep-link target) is a high-severity finding on every static analyzer.android:allowBackup="false"unless you have a deliberate backup story —trueletsadb backupcopy app data off the device.android:usesCleartextTraffic="false"(or rely on NSC) — HTTPS-only.android:debuggable="false"in release (release { isDebuggable = false }; Android Studio sets this per build type).
5. Input validation (MASVS-CODE-4)
Validate at the boundary (Repository / API client), not in the UI. Use a sealed Result<T> for
fallible operations; never let an unchecked IllegalArgumentException from a malformed server response
become a crash.
Related skills
global-skills/android/compose-feature-scaffold/SKILL.md— wire the validated repository + Ktor/OkHttp client this checklist hardens.global-skills/android/android-testing-patterns/SKILL.md— negative/boundary tests (injection, malformed tokens, token-leakage-in-logs) that prove these controls.global-skills/apple/apple-security-patterns/SKILL.md— the iOS twin (Keychain, ATS, pinning) for a cross-platform app.
Sources
- Network Security Configuration · Keystore system · DataStore release notes (datastore-tink) · Google Tink
- Google OAuth WebView policy · AppAuth-Android · OWASP MASVS
Last verified: 2026-06-03 against developer.android.com (NSC + Keystore + DataStore-tink live),
Google OAuth WebView policy, AppAuth-Android (RFC 8252), OWASP MASVS. EncryptedSharedPreferences
confirmed deprecated (androidx.security:security-crypto 1.1.0 terminal).
Re-check after: Google I/O 2026 + next Compose BOM major, or by 2026-12-01. Decay risk: high
(secrets-storage guidance and pinning APIs shift).
Found a drift? Run /skill-pattern-freshness-audit android.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.