OWASP Mobile Top 10
The OWASP Mobile Top 10
The Open Web Application Security Project (OWASP) publishes a list of the most critical mobile security risks. Understanding these vulnerabilities is essential for building secure Android applications.
Key Vulnerabilities
| # | Vulnerability | Description |
|---|---|---|
| M1 | Improper Credential Usage | Hardcoded API keys, weak token storage |
| M2 | Inadequate Supply Chain Security | Third-party libraries with known vulnerabilities |
| M3 | Insecure Authentication/Authorization | Missing auth checks, weak session management |
| M4 | Insufficient Input/Output Validation | SQL injection, XSS, intent injection |
| M5 | Insecure Communication | Missing TLS, no certificate pinning |
| M6 | Inadequate Privacy Controls | Collecting unnecessary PII, leaking data via logs |
| M7 | Insufficient Binary Protections | No obfuscation, no anti-tampering |
| M8 | Security Misconfiguration | Debuggable release builds, overly permissive exports |
| M9 | Insecure Data Storage | Plaintext passwords, unencrypted databases |
| M10 | Insufficient Cryptography | Weak algorithms, hardcoded keys |
Practical Prevention
M1 — Credential Usage:
// WRONG: Hardcoded key
val apiKey = "sk_live_abc123def456"
// RIGHT: Store in BuildConfig or secure storage
val apiKey = BuildConfig.API_KEY
// Or better: fetch at runtime from a secure backend
M4 — Input Validation:
// WRONG: Direct string concatenation in queries
val query = "SELECT * FROM users WHERE name = '" + input + "'"
// RIGHT: Parameterized queries
val cursor = db.rawQuery(
"SELECT * FROM users WHERE name = ?",
arrayOf(input)
)
M8 — Security Configuration:
<!-- WRONG: Debuggable in release -->
<application android:debuggable="true">
<!-- RIGHT: Let Gradle handle it -->
<application android:debuggable="${isDebug}">
M9 — Data Storage:
// WRONG: Storing password in SharedPreferences
sharedPrefs.edit().putString("password", rawPassword).apply()
// RIGHT: Hash with salt using a slow KDF
val salt = ByteArray(16).also { SecureRandom().nextBytes(it) }
val spec = PBEKeySpec(password.toCharArray(), salt, 100000, 256)
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val hash = factory.generateSecret(spec).encoded
Key Takeaway
Security is not a feature you add at the end. It must be baked into the architecture from the start.
Input Validation and Certificate Pinning
Input Validation
Every piece of data that enters your app—user input, deep link parameters, intent extras, push notification payloads—must be validated. Unvalidated input is the root cause of injection attacks, data corruption, and privilege escalation.
Validation Checklist
- Type checking — Ensure the data is the expected type
- Length limits — Reject unexpectedly long inputs
- Range validation — Check numeric values are within bounds
- Format validation — Use regex for emails, phone numbers, etc.
- Sanitization — Strip or escape dangerous characters
fun validateEmail(input: String?): Boolean {
if (input == null || input.length > 254) return false
val pattern = Pattern.compile("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")
return pattern.matcher(input).matches()
}
fun validateAmount(input: String?): Double? {
val amount = input?.toDoubleOrNull() ?: return null
if (amount < 0 || amount > 1_000_000) return null
return amount
}
Intent Validation
Malicious apps can send crafted intents to exported activities and services. Always validate intent extras.
class ReceiveActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val data = intent?.getStringExtra("url") ?: return
// Validate URL scheme and host
val uri = Uri.parse(data)
if (uri.scheme != "https" || uri.host != "yourdomain.com") {
finish()
return
}
loadUrl(uri.toString())
}
}
Certificate Pinning
Certificate pinning ensures your app only communicates with servers presenting a specific certificate or public key. It prevents MITM attacks even when the attacker controls a trusted CA.
// Using OkHttp with certificate pinning
val certificatePinner = CertificatePinner.Builder()
.add(
"api.yourdomain.com",
"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Base64 SPKI hash
)
.build()
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()
val request = Request.Builder()
.url("https://api.yourdomain.com/endpoint")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
// Handle response
}
override fun onFailure(call: Call, e: IOException) {
if (e is SSLPeerUnverifiedException) {
// Certificate mismatch — possible MITM attack
Log.e("Security", "Certificate pinning failed: ${e.message}")
}
}
})
Extracting the Public Key Hash
# Extract the certificate
openssl s_client -connect api.yourdomain.com:443 < /dev/null 2>/dev/null | \
openssl x509 -pubkey -noout | \
openssl pkey -pubin -outform der | \
openssl dgst -sha256 -binary | base64
Pinning Strategies
- Certificate pinning — Pin to the entire X.509 certificate. Simple but requires rotation when the certificate expires.
- Public key pinning — Pin to the Subject Public Key Info (SPKI). Survives certificate renewal as long as the key pair stays the same.
- Backup pins — Always include at least one backup pin. If your primary certificate is compromised, you can rotate without forcing an app update.
Code Hardening and Runtime Security
Detecting Rooted Devices
Rooted devices bypass Android's security sandbox. While you shouldn't block rooted users entirely, you should increase scrutiny for sensitive operations.
object RootDetector {
fun isDeviceRooted(): Boolean {
// Check for common root binaries
val paths = arrayOf(
"/system/app/Superuser.apk",
"/system/bin/su",
"/system/xbin/su",
"/data/local/xbin/su",
"/data/local/bin/su"
)
for (path in paths) {
if (File(path).exists()) return true
}
// Check for test-keys build
val buildTags = android.os.Build.TAGS
if (buildTags?.contains("test-keys") == true) return true
// Check if su command is available
return try {
Runtime.getRuntime().exec("su").waitFor() == 0
} catch (e: Exception) {
false
}
}
}
ProGuard Log Stripping
# Remove all Log calls in release builds
-assumenosideeffects class android.util.Log {
public static int v(...);
public static int d(...);
public static int i(...);
}
Secure Data Deletion
When clearing sensitive data, overwrite memory buffers before releasing them.
fun secureClear(buffer: ByteArray) {
java.util.Arrays.fill(buffer, 0.toByte())
}
fun clearAuthToken() {
val token = getToken()
token?.toByteArray()?.let { secureClear(it) }
securePrefs.edit().remove("auth_token").apply()
}
Network Security Configuration
Android 7.0+ allows declarative network security rules without code changes.
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.yourdomain.com</domain>
<pin-set expiration="2026-12-31">
<pin digest="SHA-256">base64_hash_here=</pin>
<pin digest="SHA-256">backup_hash_here=</pin>
</pin-set>
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</domain-config>
<!-- Block cleartext traffic in release -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
Reference it in the manifest:
<application
android:networkSecurityConfig="@xml/network_security_config">
</application>
Security Checklist for Production
- isDebuggable = false in release manifest
- Cleartext traffic blocked via network security config
- Certificate pinning enabled for all API endpoints
- No sensitive data in logs — strip with ProGuard
- All exports restricted — use
android:exported="false"unless necessary - Input validated — all external data sanitized
- Encryption at rest — EncryptedSharedPreferences or SQLCipher
- Obfuscation enabled — R8 minification for release builds
- Dependency audit — no known vulnerabilities in third-party libraries
- No hardcoded secrets — API keys, passwords stored securely
Quiz
1. According to OWASP Mobile Top 10, which vulnerability covers hardcoded API keys and weak token storage?
2. What is the main advantage of public key pinning over certificate pinning?
3. Why should you include backup pins when implementing certificate pinning?
4. How do you block cleartext HTTP traffic declaratively in Android 7.0+?
Flashcards
Question
What does OWASP M4 cover?
Click to reveal answer
Answer
Insufficient Input/Output Validation — includes SQL injection, XSS, intent injection, and other attacks that exploit unvalidated external data.
Question
What is certificate pinning and why is it important?
Click to reveal answer
Answer
Certificate pinning ensures your app only communicates with servers presenting a specific certificate or public key, preventing man-in-the-middle attacks even when an attacker controls a trusted CA.
Question
How do you prevent sensitive data leakage via logs in release builds?
Click to reveal answer
Answer
Use ProGuard/R8 with -assumenosideeffects to strip Log.v(), Log.d(), and Log.i() calls from the release APK at build time.
Question
Why use the network_security_config.xml file instead of code-based configuration?
Click to reveal answer
Answer
Network security config is declarative, version-independent (no code changes for new Android versions), and can be overridden per build type. It supports certificate pinning, cleartext blocking, and custom trust anchors without code.
Revision Notes
Key Takeaways
- 1. OWASP Mobile Top 10 is the reference for mobile security — know the categories and mitigations
- 2. Always validate and sanitize external input including intents, deep links, and API responses
- 3. Certificate pinning with backup pins protects against MITM attacks including rogue CAs
- 4. Strip logging statements in release builds using ProGuard -assumenosideeffects
- 5. Use network_security_config.xml for declarative, version-independent network security
Interview Tips
- • Be ready to name at least 5 OWASP Mobile Top 10 vulnerabilities and their fixes
- • Explain the difference between certificate pinning and public key pinning
- • Discuss how to handle certificate rotation without forcing app updates (backup pins)
- • Know how to block cleartext traffic and why it matters
- • Explain why hardcoded credentials are dangerous even in compiled APKs
Cheat Sheet
Secure Coding Practices Cheat Sheet
OWASP Mobile Top 10:
- M1: Improper Credential Usage — no hardcoded keys
- M4: Insufficient Input Validation — parameterized queries, sanitize all input
- M5: Insecure Communication — TLS + certificate pinning
- M7: Insufficient Binary Protections — enable R8 obfuscation
- M9: Insecure Data Storage — encrypt at rest
Input Validation:
- Validate type, length, range, and format
- Parameterized queries for all database operations
- Validate intent extras and deep link parameters
- Validate URL schemes and hosts
Certificate Pinning:
- OkHttp: CertificatePinner.Builder()
- Network Security Config: pin-set in XML
- Always include backup pins
- Prefer public key pinning over certificate pinning
Code Hardening:
- Strip logs with -assumenosideeffects
- Block cleartext via network security config
- Detect rooted devices for sensitive operations
- Secure data deletion with buffer overwriting
Production Checklist:
- isDebuggable=false
- No cleartext traffic
- Cert pinning on all endpoints
- No sensitive data in logs
- All exports restricted