Injection, traversal, deserialization, and logging leaks
Every external value—intent extra, URL, QR/NFC/BLE payload, file, server JSON, clipboard, database import—is untrusted. Validation should define an allowlisted grammar, bounds, and business authorization.
SQL injection
// ❌ User input changes query grammar.
db.rawQuery("SELECT * FROM users WHERE name = '$name'", null)
// ✅ Parameters keep data separate from SQL syntax.
db.rawQuery("SELECT * FROM users WHERE name = ?", arrayOf(name))
Room parameters are safe for values, but dynamic table/column/order clauses cannot be parameterized the same way. Map an enum to fixed SQL fragments; never paste arbitrary sort/filter expressions.
Path traversal
fun safeChild(base: File, untrustedName: String): File {
require(untrustedName.matches(Regex("[A-Za-z0-9._-]{1,80}")))
val child = File(base, untrustedName).canonicalFile
val root = base.canonicalFile
require(child.toPath().startsWith(root.toPath()))
return child
}
For ZIP extraction, validate every entry’s canonical destination, entry count, compressed/expanded bytes, nested depth, symlinks, and total time. URL encoding and alternate separators complicate naive contains("..") checks.
Unsafe deserialization
Do not deserialize attacker-controlled polymorphic class names or Java serialization graphs. Parse into constrained DTOs, disable unknown polymorphic types unless explicitly required, bound nesting/collections/string sizes, and map DTOs through business validation.
@Serializable
data class ImportRow(val id: String, val amountMinor: Long)
fun ImportRow.validate() {
require(id.matches(Regex("[A-Z0-9-]{1,40}")))
require(amountMinor in 0..10_000_000)
}
Schema-valid is not authorized: an amount within range may still exceed the current user’s limit.
Logging leaks
// ❌
logger.info("login token=$token email=$email response=$body")
// ✅
logger.info("login completed accountHash={} result={} traceId={}",
stableNonReversibleMetricId, resultCode, traceId)
Redact tokens, cookies, passwords, OTPs, full URLs/query strings, financial/health/location data, message bodies, file paths/names, BLE/NFC payloads, and cryptographic material. Crash reporters, analytics, network interceptors, screenshots, clipboard, notifications, and support exports are also data egress.
Developers/testers may sign into production-like accounts. Make redaction the logger’s default and require explicit safe fields; do not rely only on “release logging is off.”
Denial-of-service bounds
Limit request/file/body size, image pixels, archive expansion, JSON depth, list counts, regex complexity, concurrent operations, retries, and processing time. A parser that is injection-safe can still exhaust memory/CPU/disk.
Sources
Exercise
Design an offline ZIP/JSON import pipeline. Specify byte/count/depth/time limits, extraction rules, DTO validation, database transaction behavior, duplicate policy, logging, and cleanup after process death.