Lesson 2 of 6 · 9 min read
Protecting Data on Mobile Devices
In this lesson
TL;DR: Store less: short-lived tokens in memory, one refresh token or device key on disk. Keep keys in hardware: Android Keystore (TEE or StrongBox) and the iOS Keychain or Secure Enclave, non-exportable, optionally requiring user authentication. Encrypt what persists with those keys and exclude secrets from backups. Hardware backing stops a key being copied; it does not stop a compromised app using it. Then close the side doors, where most leaks happen: logs, crash reports, SDKs, backups, screenshots, clipboard, keyboard caches, URLs and notifications.
Before you read
Pick an answer before you read further. The lesson below explains it, and guessing first, right or wrong, helps the explanation stay with you.
1An app stores its refresh token, base64-encoded, in SharedPreferences. What protects it?
Decide what must exist on the device
Most apps need a way to stay signed in and nothing else. Access tokens live minutes and stay in memory; a refresh token or a device-bound key persists. Never ship long-lived server secrets in the app; no storage mechanism protects a value every user already has.
Platform storage
Android.
- Generate keys inside the Keystore (
KeyGenParameterSpec); request StrongBox for a separate secure chip where available. Keys are non-exportable; key attestation proves to a server that a key lives in hardware. - Encrypt the refresh token with an AES-GCM Keystore key before writing it to app-private storage.
setUserAuthenticationRequiredmakes a key usable only after biometric or device credential, for high-value operations.- Exclude sensitive files from backup and device transfer with backup rules.
iOS.
- Keychain items with deliberate accessibility classes:
WhenUnlockedThisDeviceOnlyfor most secrets (unavailable while locked, never migrates through backups);AfterFirstUnlockonly if background refresh needs it. - Secure Enclave keys (P-256) for signing and key agreement; the private key never leaves the coprocessor; access control can require biometry or passcode.
- File protection classes for files that hold sensitive data.
iOS Data Protection classes: when is a file readable?
iOS encrypts every file with its own key, and wraps that key with a class key. The class decides when the class key is available, which decides when the file can be read. The four classes, from Apple's Platform Security guide:
| Class | Constant | Class key available | Typical use |
|---|---|---|---|
| A: Complete Protection | NSFileProtectionComplete | Only while unlocked; discarded 10 seconds after lock (with "Require Password: Immediately") | Secrets the app only needs in the foreground |
| B: Protected Unless Open | NSFileProtectionCompleteUnlessOpen | New files can be written while locked; a file already open stays readable until closed | Downloading an attachment in the background |
| C: Until First User Authentication | NSFileProtectionCompleteUntilFirstUserAuthentication | From the first unlock after boot until the next reboot | The default for app data not assigned another class |
| D: No Protection | NSFileProtectionNone | Always; protected only by the device's hardware UID key | Mainly enables fast remote wipe |
The practical consequence is the one interviewers want: Class C protects against an attacker who has to reboot the phone, and nothing else. A phone that was unlocked once this morning and then locked has its Class C keys in memory. If your refresh token sits in a default-class file, "the phone is locked" does not protect it.
- 1
1 · Powered on, never unlocked
before first unlock
- Class A, B, C files: unreadable
- Class D files: readable
Strongest state for a seized or stolen phone
user unlocks - 2
2 · Unlocked and in use
user is holding it
- Every class readable
- Device security is the user's session
Malware or a coerced user sees everything
user locks - 3
3 · Locked after first unlock
the usual state in a pocket
- Class A: unreadable 10 s after lock
- Class C: still readable
- Class B: open files only
Default-class data is exposed here
restart - 4
4 · Rebooted
keys dropped from memory
- Back to the first state
- Class C keys gone until the next unlock
Why forensic tools prefer a phone that was never switched off
Before first unlock
Only Class D is readable. The passcode, entangled with the hardware UID key, has not been entered.
Keychain items follow the same pattern with different names. kSecAttrAccessibleWhenUnlocked corresponds to Class A, AfterFirstUnlock to Class C. The ThisDeviceOnly variants are wrapped with the device's hardware UID key in backups, so they restore unusable on another phone. WhenPasscodeSetThisDeviceOnly items are never synced to iCloud Keychain or backed up, and are deleted if the user removes the passcode, which makes them the right home for a device-binding secret. Keychain sharing between apps is possible only for apps from the same developer, enforced through access-group entitlements.
Android: file-based encryption and the Keystore
File-based encryption (FBE) arrived in Android 7.0 and is mandatory for devices launching with Android 10 or later. It gives each app two storage areas:
- Credential Encrypted (CE) storage, the default, is available only after the user unlocks the device once since boot (the Android analogue of Class C).
- Device Encrypted (DE) storage is available during Direct Boot, before the first unlock, for things like alarms and accessibility settings. Do not put secrets there.
The Android Keystore keeps key material out of the app process entirely: the app holds a handle and sends data to be signed, encrypted or decrypted. With a Trusted Execution Environment (TEE) or StrongBox backing it, the key never leaves secure hardware, so even a compromised OS can use the key but not extract it. Details worth knowing:
- StrongBox (Android 9 and later) is a separate secure chip with its own CPU, storage, true random number generator, tamper resistance and secure timer. It supports fewer algorithms (RSA 2048, AES 128 and 256, ECDSA and ECDH on P-256, HMAC-SHA256, 3DES) and is slower; Google advises using it only when the threat model needs it.
- Biometric-bound keys are invalidated by default when a new fingerprint or face is enrolled, so someone who adds their own biometric to an unlocked phone cannot use the key.
- Version binding ties keys to the OS version and patch level; rolling the device back to an older, vulnerable OS makes them unusable.
- Key attestation (since Keymaster 2 in Android 7.0) produces a certificate chain, rooted in a Google key, that lets your server verify a key really lives in hardware and with which properties. The client trust and attestation lesson covers what that proves.
What hardware backing guarantees
App
holds a key handle
Keystore or Enclave
private key inside
Server
holds the public key
What an attacker gets
Token copied from storage or logs
- No private key to sign with
- Requests from another device fail
Malware inside the app process
- Can ask the hardware to sign
- Cannot copy the key out
The app asks the hardware to sign
The private key never leaves the secure hardware; the app only gets a signature back.
Hardware backing means the key cannot be exported, even by root, so a stolen token without the key is useless when the server requires device signatures. It does not mean the key cannot be used: malware or an attacker controlling the app process on an unlocked device can ask the hardware to sign or decrypt. Requiring user authentication per use narrows that window.
Backups: the copy you forgot about
Backups are a second storage location with different protection, and they are easy to overlook.
- Android Auto Backup is on by default for apps targeting Android 6.0 and later (
android:allowBackupdefaults totrue) and copies up to 25 MB per app to the user's cloud account, end-to-end encrypted with the screen-lock credential from Android 9 on devices that support it. Cache directories and theno_backupdirectory are always excluded. - From Android 12, on some devices
allowBackup="false"disables cloud backup but not device-to-device transfer. UsedataExtractionRuleswith separate<cloud-backup>and<device-transfer>sections to exclude secrets from both. - Google recommends its Block Store API, rather than files or shared preferences, for credentials that should survive a phone migration.
- On iOS,
ThisDeviceOnlyKeychain items and Secure Enclave keys never usefully leave the device, so a restore onto a new phone forces re-enrolment. Design for that: it is the security property, not a bug.
The side doors
| Channel | Control |
|---|---|
| Logs and crash reports | Strip debug logging from release builds, redact, configure crash SDK scrubbing |
| Analytics and ad SDKs | Inventory, minimise, review what they send; they run with the app's access |
| Backups | Backup rules (Android), this-device-only classes and file protection (iOS) |
| App-switcher screenshots | FLAG_SECURE on Android; cover the view on iOS when backgrounding |
| Clipboard | Avoid copying secrets; clear; mark as sensitive |
| Keyboard caches and autofill | Secure text entry, no suggestions on sensitive fields |
| URLs | Tokens in headers or bodies, never query strings |
| WebView storage | Clear cookies and caches on logout |
| Notifications | Generic text on the lock screen |
Worked example: auditing a fintech app
Exercise login, view a statement and log out on a test device with a test account, then search everything for the test refresh token's value: the app's files (encrypted, good), logcat (found in a debug-level network log that ships in release: finding), the cloud backup (excluded, good), crash reporter payloads (found in a breadcrumb: finding), and analytics events (account number in a screen-view event: finding). Storage was fine; three side doors were open.
Worked example: the lost-phone matrix
A banking app persists one refresh token. Compare three ways of storing it across five situations.
| Situation | Token in a default-class (C) file | Token in a Class A file, encrypted with a hardware key | Device-bound P-256 key with per-use user authentication |
|---|---|---|---|
| Phone found powered off | Protected | Protected | Protected |
| Phone found locked, unlocked earlier today | Exposed to anyone who can read storage | Protected (Class A key discarded 10 s after lock) | Protected |
| Unlocked; malware in a different app | Protected by the sandbox | Protected by the sandbox | Protected by the sandbox |
| Malicious SDK inside the banking app | Exposed | Exposed (the app can decrypt) | Key usable only with the user's biometric, never copyable |
| Owner roots the phone | Exposed | Usable on this device, not copyable | Usable on this device with authentication, not copyable |
The table shows what each layer buys. Class A narrows the time window. The sandbox handles other apps. Nothing on the device stops code inside the app from using what the app can use, but hardware keys stop it being copied, so the server sees every request come from one enrolled device and can revoke that device alone. Add a sixth row for backups: Auto Backup would copy the default-class file unless excluded, while the hardware key restores unusable elsewhere.
Misconceptions interviewers listen for
- "Data Protection is on, so files are safe on a stolen phone." Default-class data is readable after the first unlock until reboot.
- "Hardware-backed means the key can't be misused." It means it can't be exported. Code in the app, or a compromised OS, can still ask for operations.
- "allowBackup=false stops data leaving the device." Not for device-to-device transfer on some Android 12+ devices; use data extraction rules.
- "Encrypted storage means we're done." Storage is one channel; logs, SDKs, screenshots and URLs are the usual leaks.
- "StrongBox is always better than the TEE." It resists physical attack better and is slower with fewer algorithms. Choose per threat model.
What if · interview follow-ups
Hold your answer as the constraints change
You are reviewing how a banking app stores its refresh token and device credentials on Android and iOS. The interviewer changes one condition at a time.
Start here
What if the token is in an ordinary app file with the default protection and the phone is stolen while locked?
Has the phone been unlocked since it last booted?
If it has, which is the normal state, default-class iOS data and Android credential-encrypted storage are decryptable by the device, so anyone who can get code running or read storage on that phone can reach the token. Only a reboot drops those keys. Put the token in a Class A file or a WhenUnlocked Keychain item encrypted with a hardware key, so it becomes unreadable seconds after lock.
Carry it forwardAsk which keys are in memory in each lock state; 'encrypted at rest' usually means 'until first unlock'.
Weak answerSaying the device's full-disk encryption protects a locked phone in all cases.
Constraint changes
What if the app must refresh the token in the background while the phone is locked?
Which protection class is available while locked?
Background work needs a class available after first unlock: AfterFirstUnlock in the Keychain or credential-encrypted storage on Android. Narrow it to that one item and keep everything else in the stricter class. Better, ask whether a background refresh is needed at all, or whether a short-lived token can be fetched at the next foreground.
Carry it forwardPick protection per item from the moment it must be readable, and keep the exceptions minimal.
Weak answerMoving the whole app to the weaker class because one feature needs it.
Constraint changes
What if a third-party SDK inside the app is malicious?
Is the SDK inside or outside the sandbox?
It runs as the app, so it can read anything the app can read and ask the Keystore or Secure Enclave to use the app's keys. It cannot export hardware keys. Require user authentication per use for the keys that approve payments, show the transaction details in a confirmation the user approves, and keep the SDK inventory small and reviewed, including what it sends over the network.
Carry it forwardCode in your process has your authority; hardware custody limits copying, not use.
Weak answerBelieving the Keystore protects keys from code inside the app.
Constraint changes
What if the user moves to a new phone?
Which credentials should follow them, and which must not?
Device-bound keys in hardware and ThisDeviceOnly Keychain items do not migrate, so the new phone must enrol again with the server, which is exactly the signal you want for a high-risk event. Exclude the refresh token from cloud backup and device transfer with backup rules, and if a smoother migration is required for low-risk data, use the platform's credential backup API rather than raw files.
Carry it forwardA credential that cannot be restored elsewhere turns a new device into an explicit, auditable enrolment.
Weak answerIncluding secrets in backups to avoid a re-login.
Constraint changes
What if an attacker rolls the device back to an older OS with a known Keystore bug?
What are hardware keys bound to besides the device?
Keystore keys are bound to the OS version and patch level, so after a rollback they become unusable and the app must re-enrol, which the server sees. Combine that with attestation that reports the patch level and verified-boot state, and refuse high-value operations from devices that fall below your minimum.
Carry it forwardVersion binding converts a downgrade attack into a visible re-enrolment.
Weak answerAssuming a rollback silently keeps every key working.
Now defend it
As the defender, how would you prove the app's data protection works before release?
Use a test account and a canary token value, exercise every flow, then search the app's files in each lock state, device logs, crash-report payloads, analytics events, network captures, cloud backups and device-transfer archives for the canary. Verify Keychain and file classes on a test device, and check that device keys report hardware backing through attestation.
Primary references
References for the mechanisms discussed above, accessed 6 November 2026:
How this shows up in interviews
"How do you store tokens on mobile?" and "where does data leak from a mobile app?" are common. Interviewers check deliberate storage choices, an exact statement of what hardware backing protects, and whether you look past storage to logs, SDKs and backups.
Say it in 30 seconds: "Store as little as possible: short-lived tokens in memory and one refresh token or device key on disk. Keys go in the Keystore or Keychain and Secure Enclave, non-exportable and, for high-value use, behind user authentication; secrets are encrypted with them and excluded from backups. Hardware means the key can't be copied, not that it can't be used. Then I check the side doors, because that's where leaks actually happen: logs, crash reports, SDKs, backups, screenshots, clipboard and URLs."
Check yourself
Say or write your answer first, then reveal. Explaining it from memory, in your own words, is the skill an interviewer is checking.
1Why prefer WhenUnlockedThisDeviceOnly over AfterFirstUnlock for a Keychain item holding a session secret?
2A pentester finds the session token in an analytics event payload. Which control failed, and how do you fix it?
3What does iOS Data Protection Class C protect against, exactly?
4Why are Android Keystore keys bound to the OS version and patch level?
5What is the difference between credential-encrypted and device-encrypted storage on Android?