Install
$ agentstack add skill-guillemroca-agent-skills-android-security-and-hardening ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Security and Hardening
Overview
Security is a development constraint, not an afterthought. This skill provides a three-tier framework — Always Do, Ask First, Never Do — covering the OWASP Mobile Top 10, Android-specific attack vectors, and hardening patterns for production apps.
When to Use
- Handling user credentials, tokens, or personal data
- Implementing network communication
- Before shipping any release to the Play Store
- Reviewing code that touches authentication or authorization
- Setting up ProGuard/R8 rules
- Implementing WebView features
Skip when: Changes are purely cosmetic with no data or network impact.
Core Process: Three-Tier Framework
Always Do
- Secure data storage. Jetpack Security Crypto (
EncryptedSharedPreferences/MasterKey) is deprecated and unmaintained — do not add it to new code. Encrypt with a key held in the Android Keystore and persist the ciphertext (DataStore or a file):
// Key lives in the Android Keystore — never leaves secure hardware
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
keyGenerator.init(
KeyGenParameterSpec.Builder("auth_token_key", KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build()
)
val secretKey = keyGenerator.generateKey()
// Encrypt, then persist iv + ciphertext (e.g. in Proto DataStore)
val cipher = Cipher.getInstance("AES/GCM/NoPadding").apply { init(Cipher.ENCRYPT_MODE, secretKey) }
val encrypted = cipher.iv + cipher.doFinal(token.toByteArray())
Existing apps already on EncryptedSharedPreferences can keep it (the format is stable), but plan a migration and never store new categories of secrets with it.
- Network Security Config:
api.example.com
base64EncodedPin=
base64EncodedBackupPin=
Targeting API 37 (Android 17) also changes the network/auth surface: LAN access requires the ACCESS_LOCAL_NETWORK permission, standard SMS OTPs are delayed 3 hours (use the SMS Retriever API or SMS User Consent instead of reading raw SMS), and Encrypted Client Hello + Certificate Transparency are on by default for TLS — verify pinning still works.
- Input validation:
// Validate all external input
fun processDeepLink(uri: Uri): DeepLinkResult {
val taskId = uri.getQueryParameter("taskId")
?: return DeepLinkResult.Invalid("Missing taskId")
// Validate format
if (!taskId.matches(Regex("^[a-zA-Z0-9-]{1,36}$"))) {
return DeepLinkResult.Invalid("Invalid taskId format")
}
return DeepLinkResult.Valid(taskId)
}
- Intent validation for exported components:
// Validate intents for exported Activities/Receivers
class TaskDeepLinkActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = intent ?: run { finish(); return }
// Validate the source and data
val taskId = intent.data?.getQueryParameter("taskId")
if (taskId == null || !isValidTaskId(taskId)) {
finish()
return
}
// Process valid deep link
}
}
- Secrets management:
# local.properties (NEVER committed to git)
API_KEY=your_secret_key_here
// build.gradle.kts
val localProperties = Properties().apply {
val file = rootProject.file("local.properties")
if (file.exists()) load(file.inputStream())
}
android {
defaultConfig {
buildConfigField("String", "API_KEY",
"\"${localProperties.getProperty("API_KEY", "")}\"")
}
}
# .gitignore
local.properties
*.jks
*.keystore
- Remove debug code for release:
// build.gradle.kts — strip Log calls in release
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
// proguard-rules.pro
-assumenosideeffects class android.util.Log {
public static int d(...);
public static int v(...);
}
Ask First
- Changes that require review:
- Modifying authentication or authorization logic
- Adding new exported components (Activities, Receivers, Providers)
- Changing certificate pinning configuration
- Adding new permissions to AndroidManifest
- Integrating new third-party SDKs
- Implementing WebView with JavaScript enabled
- Modifying ProGuard/R8 rules
- Changing data encryption or storage approach
Never Do
- Absolute prohibitions:
| Never | Why | |-------|-----| | Commit secrets to git | Secrets in git history are permanent, even after removal | | Log sensitive data (Log.d with tokens) | Logcat is accessible to other apps on rooted devices | | Trust client-side validation alone | All client validation can be bypassed | | Use MODE_WORLD_READABLE | Any app can read your files | | Enable cleartext traffic in production | Data interceptable on network | | Store passwords in plain text | Use EncryptedSharedPreferences or Android Keystore | | Use WebView.setJavaScriptEnabled(true) without sanitizing | XSS attacks via injected content | | Export components without android:permission | Any app can invoke them | | Disable certificate verification | Man-in-the-middle attacks | | Use FLAG_SECURE to prevent screenshots AND log sensitive data | Inconsistent security posture |
WebView Security
- If you must use WebView:
webView.settings.apply {
javaScriptEnabled = true // Only if necessary
allowFileAccess = false
allowContentAccess = false
domStorageEnabled = true
// Restrict to your domain
setSupportMultipleWindows(false)
}
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
val url = request.url
// Only allow your trusted domains
return url.host != "trusted.example.com"
}
}
// Never add JavaScript interfaces that expose sensitive operations
// webView.addJavascriptInterface(dangerousObject, "Android") // DON'T
OWASP Mobile Top 10 Quick Reference
| # | Risk | Android Mitigation | |---|------|-------------------| | M1 | Improper Credential Usage | Android Keystore, EncryptedSharedPreferences | | M2 | Inadequate Supply Chain Security | Dependency verification, version pinning | | M3 | Insecure Authentication | BiometricPrompt, OAuth2 with PKCE | | M4 | Insufficient Input/Output Validation | Validate deep links, intents, API responses | | M5 | Insecure Communication | Network Security Config, cert pinning | | M6 | Inadequate Privacy Controls | Minimize data collection, respect permissions | | M7 | Insufficient Binary Protections | R8/ProGuard, tamper detection | | M8 | Security Misconfiguration | Review manifest, disable debuggable in release | | M9 | Insecure Data Storage | EncryptedSharedPreferences, Room with encryption | | M10 | Insufficient Cryptography | Android Keystore for key management |
Common Rationalizations
| Shortcut | Why It Fails | |----------|-------------| | "It's just a debug key, I'll change it later" | Debug keys committed to git stay in history forever. | | "Our API already validates input" | Client validation improves UX; server validation prevents exploits. Both needed. | | "We don't need cert pinning for v1" | v1 is when MITM attacks are most damaging — no monitoring to detect them. | | "ProGuard breaks too many things" | Configure it properly with @Keep annotations. The security cost of skipping is higher. |
Red Flags
- Secrets (API keys, tokens) in source code
android:debuggable="true"in release manifestclearTextTrafficPermitted="true"in production- No Network Security Config
Log.dwith user data or tokensMODE_WORLD_READABLEorMODE_WORLD_WRITEABLE- Exported components without permission checks
- WebView with unrestricted JavaScript
- No ProGuard/R8 in release builds
- Disabled certificate verification
Verification
- [ ] No secrets in source code (grep for API keys, tokens, passwords)
- [ ] Network Security Config present and enforces HTTPS
- [ ] Certificate pinning for production API
- [ ] EncryptedSharedPreferences for sensitive data
- [ ] Input validated at all system boundaries
- [ ] Exported components have permission checks
- [ ] ProGuard/R8 enabled for release builds
- [ ] Debug logging stripped in release (ProGuard rules)
- [ ]
local.propertiesin.gitignore - [ ]
android:debuggablenot set (defaults to false for release) - [ ] WebView (if used) restricts domains and JavaScript exposure
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: GuillemRoca
- Source: GuillemRoca/agent-skills-android
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.