On this page
- 1. Hard-coded secrets
- 2. Accidentally exported components
- 3. Intent redirection
- 4. Mutable PendingIntents
- 5. Insecure WebViews
- 6. Insecure storage and backups
- 7. Weak network security
- 8. Sensitive data in logs and screenshots
- 9. Trusting the client
- 10. Path traversal with file names from outside
- Building it into your process
- Quick triage
- Frequently asked questions
- Conclusion
Most real-world Android vulnerabilities aren't exotic exploits. They're configuration and design mistakes: a component exported by accident, a token in logs, a WebView that loads anything. Here are the most common ones, how attackers use them, and how to fix them.
1. Hard-coded secrets
Risk: keys extracted from the APK and abused. Fix: keep secrets server-side, and restrict any client keys. See Secure API Keys.
2. Accidentally exported components
<!-- โ Any app can start this and reach internal functionality -->
<activity android:name=".admin.ResetDataActivity" android:exported="true" />
<!-- โ
Internal-only -->
<activity android:name=".admin.ResetDataActivity" android:exported="false" />Audit every exported="true". Exported components must validate all input and should not perform sensitive actions without authentication.
3. Intent redirection
// โ An external app passes an Intent extra that points to a private component; we launch it with our privileges
val next = intent.getParcelableExtra<Intent>("next")
startActivity(next)
// โ
Don't forward intents; use your own allow-listed destinations
when (intent.getStringExtra("destination")) {
"orders" -> startActivity(Intent(this, OrdersActivity::class.java))
else -> finish()
}4. Mutable PendingIntents
// โ
Immutable unless you truly need fill-in
PendingIntent.getActivity(context, requestCode, Intent(context, MainActivity::class.java),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)A mutable PendingIntent with an implicit base intent can be hijacked by another app to act with your app's identity.
5. Insecure WebViews
webView.settings.apply {
javaScriptEnabled = false // enable only if required by your own trusted content
allowFileAccess = false
allowContentAccess = false
}
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
val host = request.url.host ?: return true
return host != "help.example.com" // block navigation to anything else (true = we handle/block)
}
}Never combine addJavascriptInterface with untrusted content.
6. Insecure storage and backups
Risk: tokens or personal data in plain files, on external storage, or in backups. Fix: app-private storage, Keystore-backed encryption, and backup exclusions. See Encrypted Storage.
7. Weak network security
// โ Trust-all TrustManager: disables TLS protection entirely
object : X509TrustManager {
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {} // accepts anything
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
}Use the platform defaults, Network Security Config, and no cleartext traffic.
8. Sensitive data in logs and screenshots
Fix: remove debug logging of tokens and personal data from release builds, and use FLAG_SECURE on screens showing sensitive data.
9. Trusting the client
Risk: prices, feature unlocks or user IDs accepted from the app. Fix: the server computes and authorises everything, using verified identity tokens and Play Integrity for high-value actions.
10. Path traversal with file names from outside
fun safeFile(dir: File, untrustedName: String): File {
val file = File(dir, untrustedName).canonicalFile
require(file.path.startsWith(dir.canonicalPath + File.separator)) { "Invalid path" } // blocks ../../
return file
}Building it into your process
- Enable lint security checks and treat them as errors.
- Add a security section to your PR template (exports, intents, storage, logging).
- Run periodic reviews against OWASP MASVS for sensitive apps.
- Keep dependencies updated, and watch for security advisories.
Quick triage
Before every release
Grep the manifest for exported components, check logging, and confirm no secrets in the build.
New WebView feature
Host allow-list, JavaScript only if needed, no file access.
New deep link
Validate parameters, and authorise on the server.
Frequently asked questions
Does Google Play scan for these issues?
Play performs some automated checks and may warn about known vulnerable patterns, but you remain responsible for your app's security.
Is Kotlin safer than Java here?
Kotlin prevents some bugs (null safety), but these vulnerabilities are about design and configuration, which apply in any language.
Where should a small team start?
Secrets, exported components, logging and TLS. They're quick to check and cover many real incidents.
Conclusion
Most Android vulnerabilities come from untrusted input reaching privileged code or sensitive data leaking out. Minimise exports, never forward intents, use immutable PendingIntents, lock down WebViews and TLS, protect storage and logs, and keep the server as the source of truth.
Next step: ship your secure app with How to Publish an Android App on Google Play.
Official references
๐ฑ Apps built by Yunok Tech
Real Android apps on Google Play, free to download.
NotiVaultNotification history: recover dismissed notificationsGet it โธ
Walzi4K wallpapers for lock and home screenGet it โธ
Ready to build real Android applications?
Security is easiest to get right when it is part of how you build from the first screen. Strengthen the Android fundamentals that secure, privacy-respecting apps are built on.
Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.
Explore the Android course and take your next step as an Android developer.