Course outline

Android development / Advanced: reliable mobile systems and releases / Lesson 16

Security and privacy boundaries

Reduce exposure through explicit trust boundaries, minimal collection, and appropriate platform key management.

About 4 min readContent reviewed
On this page

Lesson goals

  • Identify untrusted entry points and protect sensitive data.
  • Explain the limits of sandboxing, Keystore, and client-side checks.

Name assets and callers

Begin with what must be protected: authentication tokens, private reading notes, account identity, signing keys, and pending operations. Then identify who can influence inputs: another app, a network service, a deep link, a restored backup, or a user on a shared device. Androidโ€™s sandbox is useful, but exported components, granted URIs, logs, backups, and server requests cross its boundary. A threat model should lead to concrete controls and test cases. Do not promise that obfuscation or a hidden screen makes a secret embedded in an APK confidential.

Validate and authorize every external entry

A deep link can contain a syntactically valid id for a record the current account cannot access. Validation checks shape; authorization checks authority. Perform both at the appropriate layer and enforce server-side authorization independently of the app. Allowlist expected schemes and hosts for externally supplied links rather than accepting any URI. Parameterize database queries, restrict exported components, and avoid passing untrusted intent objects onward with all their flags. Safe defaults should lead to a recoverable rejection, not an implicit attempt to interpret arbitrary input as an internal command.

kotlin A narrow synthetic deep-link parser
fun allowedBookLink(uri: Uri): String? {
    if (uri.scheme != "https" || uri.host != "reading.example.com") return null
    val parts = uri.pathSegments
    if (parts.size != 2 || parts[0] != "books") return null
    return parts[1].takeIf { it.matches(Regex("[A-Za-z0-9_-]{1,80}")) }
}

Import android.net.Uri. reading.example.com is a documentation-only example domain, not a configured service. This accepts a small path contract; the repository and backend must still authorize the returned id. Verified App Links require separate domain association configuration.

Use Keystore for key material

Android Keystore can hold non-exportable key material and restrict allowed operations. It does not make every use of a key safe, and hardware backing varies by device and configuration. For sensitive local encryption, choose a reviewed authenticated-encryption design and handle key invalidation and recovery. The example generates an AES key on API 23 or higher; generation should not overwrite an existing alias casually. Keystore is not a place to hide a shared backend master secret shipped to every device, and encryption does not replace access control or thoughtful backup policy.

kotlin Key generation, not a complete encryption system
@RequiresApi(23)
fun generateNotesKey(alias: String): SecretKey {
    val generator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"
    )
    generator.init(KeyGenParameterSpec.Builder(
        alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
    ).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
     .build())
    return generator.generateKey()
}

Import Android Keystore classes, androidx.annotation.RequiresApi, and javax.crypto types. Guard calls on older supported devices. A complete AES-GCM design must generate a fresh nonce per encryption with a given key, preserve nonce and authentication tag, and define key-loss recovery.

Collect less and retain deliberately

Private notes should not appear in diagnostic logs, analytics properties, notification previews, or crash breadcrumbs by default. Prefer event categories and opaque operation identifiers, and define retention and deletion for each data store. Review backup and device-transfer rules so sensitive material is not restored into an unusable or unintended context. Logout should clear account-scoped state and invalidate pending operations according to policy, rather than merely replacing the visible screen. Treat third-party SDK collection as part of the appโ€™s behavior and keep disclosures consistent with what the code actually sends.

Test rejection and recovery

Exercise malformed links, cross-account ids, expired credentials, missing keys, denied permissions, and restored data from an older version. Inspect the merged manifest and release network configuration, not only debug behavior. Dependency updates need source verification and compatibility review; a security-sounding library name is not evidence of suitability. Keep release signing secrets outside source control and restrict access in CI. A useful security result describes the protected asset, the attempted boundary crossing, the observed rejection, and the recovery offered to an authorized user.

Common errors and fixes

A client-side check is treated as authorization

Why it happens
The backend trusts an id supplied by the app.
How to fix it
Enforce account ownership on every protected server operation.
Prevent it next time
Test cross-account access independently of UI visibility.

Encryption cannot recover after device transfer

Why it happens
Encrypted files were restored without a usable key.
How to fix it
Define supported backup exclusions or an authenticated recovery path.
Prevent it next time
Test restore and key invalidation before release.

Debug logs reveal private titles or tokens

Why it happens
Raw request or domain objects were logged.
How to fix it
Remove sensitive payloads and use redacted structured events.
Prevent it next time
Review logging as part of data collection.

Check your understanding

1. A link parser accepts a valid record id. Explain the next two checks before displaying private notes.

Show worked solution

Confirm the current authentication state and authorize that account for the record through the trusted data/server contract. A valid path proves only syntax. If access is absent, reject without leaking record details and offer an appropriate sign-in or navigation path. Do not rely on a hidden button to prevent direct entry.

2. An app proposes storing an API master secret in BuildConfig and obfuscating release code. Evaluate the plan.

Show worked solution

The secret still ships to every client and can be extracted or used from a compromised app. Keep privileged secrets on trusted infrastructure and issue appropriately scoped, revocable credentials to authenticated clients. Obfuscation can raise reverse-engineering effort but does not create a secure secret-storage boundary for a shared server credential.

Key takeaways

  • Validation, authorization, and confidentiality are separate controls.
  • Keystore protects key material within a defined threat model.
  • Minimization and recovery policy are part of security design.

Official references