Android Security: Kotlin Coding Practices & Intent Handling

πŸ”§ Kotlin & Android Coding Terms You Must Know

  • ?.let {}: Kotlin null-safe scope function. Prevents crashes if an object is null; not a security check.

  • getCallingPackage(): Identifies the app that sent an intent. Use this to verify the intent source.

  • checkCallingOrSelfPermission(): Checks if a permission is declared. Does not verify the actual identity of the sender.

  • resolveActivity(intent, 0): Checks if the intent can be handled. Prevents app crashes, but not a security check.

  • putExtra() / getStringExtra(): Used to send/get data via intents. Can be spoofed if not validated.

  • android:exported="true": Allows external apps to access a component. Must be paired with permissions or signature checks.

  • android:permission="...": Declares the required permission for component access. Best practice for services and receivers.

  • Explicit Intent: Targets a known component. Safer, no hijacking.

  • Implicit Intent: System matches via filters. Can be hijacked or intercepted.

  • BroadcastReceiver: Listens for system/app events. Can be abused if exported & unprotected.

  • Service: Background task handler. Must verify who sends commands (onStartCommand()).


πŸ”’ Encryption & Authentication Distinctions

  • FDE (Full-Disk Encryption): Encrypts all at once with a single key. Unlocks everything at once; no file-level control.

  • FBE (File-Based Encryption): Encrypts each file with its own key. Allows selective access (e.g., alarms before unlock).

  • HMAC: Ensures integrity, not confidentiality. Cannot encrypt or decrypt data.

  • AES: Symmetric encryption for data protection. Used to encrypt/decrypt entire payloads.

  • JWT: Signed token used in authentication. Stateless, hard to revoke without server state.

  • Nonce: One-time-use number. Stops replay attacks.

πŸ›‘οΈ Secure Intent Handling Rules

  • Always verify:

    • getCallingPackage() or getCallingUid()

    • Permissions via checkCallingOrSelfPermission()

    • Whether the intent can be handled via resolveActivity()

  • Secure intent pattern example:

incomingIntent?.let { intent ->
if (packageManager.resolveActivity(intent, 0) != null &&
checkCallingOrSelfPermission("com.example.SAFE_PERMISSION") == PackageManager.PERMISSION_GRANTED &&
getCallingPackage() == "com.trusted.app") {
processIntent(intent)
}
}
  • Don’t:

    • Rely on ?.let {} for security

    • Trust extras without checking the source

    • Export components without protection


⚠️ High-Risk Misconceptions to Avoid

  • ❌ HMAC encrypts data β†’ HMAC only checks integrity

  • ❌ Kotlin let{} validates sender β†’ It just checks for null

  • ❌ Implicit intents are always safe β†’ They can be hijacked by any app with a matching filter

  • ❌ A signed JWT cannot be reused β†’ It can, unless short-lived or bound to a nonce

  • ❌ Logging tokens to Logcat is fine β†’ Logcat can be accessed on rooted/debug builds

  • ❌ Certificate pinning is optional β†’ Without it, MITM with a rogue CA is possible

  • ❌ Bootloader fails β†’ kernel loads anyway β†’ Secure boot halts the process on any failure

  • ❌ RASP replaces other layers β†’ It complements OS/hardware protections, doesn’t replace them


🧠 Biometric & Hardware Memory Aids

  • TEE (Trusted Execution Environment): Handles biometric processing and secure key storage.

  • HSM (Hardware Security Module): Offloads crypto, prevents physical key extraction.

  • Secure Boot Chain: Must validate Bootloader β†’ Kernel β†’ System.

  • MAC Address Randomization: Prevents device tracking via Wi-Fi/Bluetooth.


πŸ“± Intent-Related Attacks to Know

  • Intent Spoofing: A fake app sends a malicious intent to a component.

  • Intent Hijacking: Another app intercepts an intent meant for your component.

  • Broadcast Injection: A malicious app sends a broadcast your app reacts to.

  • Service Hijack: A malicious app invokes exported service methods (e.g., deleteUser()).


πŸ§ͺ Replay Attack Mitigation Checklist

  • βœ… Use short token lifetimes

  • βœ… Include a nonce or timestamp

  • βœ… Store tokens in hardware-backed keystore

  • ❌ Don’t store in plain text

  • ❌ Don’t trust tokens after logout or timeout

βœ… Key Kotlin Coding Lessons

  1. Validate All Incoming Intents

kotlin
incomingIntent?.let { intent ->
if (packageManager.resolveActivity(intent, 0) != null &&
checkCallingOrSelfPermission("com.example.SAFE_PERMISSION") == PackageManager.PERMISSION_GRANTED &&
getCallingPackage() == "com.trusted.sender") {
processIntent(intent)
}
}
  • Defends against spoofed intents, broadcast injection, and hijacking.

  1. Avoid Sending Sensitive Data via putExtra() Unless Secured

kotlin
val intent = Intent(this, DashboardActivity::class.java).apply {
putExtra("session_token", sessionToken) // Risky if not encrypted or validated
}
  • Extras can be read by malicious apps if intercepted.

  1. Explicit vs. Implicit Intents

kotlin

// Explicit (Safe) val intent = Intent(this, TargetActivity::class.java) startActivity(intent) // Implicit (Risky) val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://nyu.edu")) startActivity(intent)

  • Use explicit for internal navigation.

  • Validate implicit before processing.

🧱 Intent Verification Checklist

  • Confirm intent is not null.

  • Use resolveActivity() to check if the component exists.

  • Use checkCallingOrSelfPermission() to check permissions.

  • Use getCallingPackage() or getCallingUid() to verify the sender.


πŸ›‘οΈ Signature-Level Permissions

xml


  • Only apps signed with the same certificate can access the component.


πŸ” Token Handling Best Practices

  • Store tokens in Android Keystore or EncryptedSharedPreferences

  • Never log tokens (Log.d(), println())

  • Use short-lived and refreshable tokens


πŸ“‘ Secure BroadcastReceiver Pattern

kotlin
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.getStringExtra("msg") != null &&
context.checkCallingOrSelfPermission("com.example.ALARM_PERMISSION") == PackageManager.PERMISSION_GRANTED) {
showAlarm(intent.getStringExtra("msg")!!)
}
}
}
  • Add the corresponding android:permission in the manifest to protect access.


⚠️ Common Mistakes to Spot on the Exam

  • Using intent.getStringExtra() without checking the sender β†’ Spoofable

  • Logging tokens or user data with Log.d() β†’ Sensitive info leak

  • Storing tokens in plain SharedPreferences β†’ Easily accessible

  • Calling startActivity() on a null intent β†’ App crash risk