All resources
// Resources

iOS Keychain and Data Protection Review Checklist

Published July 10, 2026

iOS uses separate controls for secrets and files. Keychain Services stores small credentials, keys, and certificates with item accessibility and entitlement-based access groups. Data Protection assigns file protection classes whose availability changes with device lock state. Platform encryption is not one storage policy. Review each secret and file against required availability, migration behavior, sharing boundary, and recovery flow.

Classify data before API choice

Inventory refresh tokens, private keys, biometric-gated secrets, cached documents, databases, exports, logs, and queued background work. Record why data exists, maximum exposure if disclosed, required availability while locked, retention period, backup/migration requirement, and every target that can read it. Remove data no feature needs; encryption does not repair unnecessary retention.

Use Keychain for small secrets rather than UserDefaults or application files. Choose kSecAttrAccessibleWhenUnlocked when item must be unavailable while device is locked. kSecAttrAccessibleAfterFirstUnlock permits access after first post-boot unlock, including later lock states, which can be needed for background work. ThisDeviceOnly variants do not migrate during backup restore or device transfer. kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly requires device passcode and item disappears when passcode is removed. Deprecated Always classes are not a safe fallback.

let query: [String: Any] = [
  kSecClass as String: kSecClassGenericPassword,
  kSecAttrAccount as String: "refresh-token",
  kSecValueData as String: tokenData,
  kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
let status = SecItemAdd(query as CFDictionary, nil)

Do not label every secret “unavailable while locked.” That is true only for chosen accessibility class. Handle expected Keychain errors without replacing protected secret with weaker storage.

Inspect access-group boundary

Each Keychain item belongs to one access group. Default group derives from signed application entitlement; Keychain Sharing allows targets from same development team to use configured shared group. Review .entitlements for keychain-access-groups, every app, extension, or helper entitled to each group, and actual kSecAttrAccessGroup use. An app lacking entitlement for a selected group receives errSecMissingEntitlement.

App Groups and Keychain Access Groups solve different problems. App Groups share containers and selected IPC; they are not a substitute for a narrow Keychain sharing review. Keep secrets in private default group unless a documented target needs them. Shared credential group expands blast radius to every trusted signed target.

Test file protection separately

Data Protection applies to ordinary files. NSFileProtectionComplete makes file unavailable soon after lock. NSFileProtectionCompleteUnlessOpen permits already-open files after lock but blocks new access. NSFileProtectionCompleteUntilFirstUserAuthentication remains usable after first unlock until reboot. NSFileProtectionNone still benefits from hardware encryption but is available while device powered on. Select class from real workflow, not generic “strongest” label.

try data.write(to: url, options: .atomic)
try FileManager.default.setAttributes(
  [.protectionKey: FileProtectionType.complete], ofItemAtPath: url.path
)

Test fresh boot before first unlock, unlocked state, locked state, background task, backup/restore expectation, extension access, and app update. Do not state reinstall behavior as universal guarantee; validate supported OS and deployment path.

Review record

ArtifactEvidenceOwnerResultExceptionRemediation
Keychain itemattributes and lock-state testiOS ownerpass/failrequired background tokenchoose explicit class
Access groupsigned entitlementsrelease ownerpass/failapproved extensionremove excess target
Protected fileclass plus device-state testdata ownerpass/failqueued uploadchange file class
Migrationrestore test and requirementproduct ownerpass/faildocumented recoveryuse ThisDeviceOnly

Decision rule: choose Keychain accessibility and file class from documented availability need; choose sharing only for verified entitled targets; treat migration as separate requirement.

Remediate and retest

Move secrets out of preferences and logs, delete obsolete entries, make accessibility explicit, narrow access groups, and assign file protection before sensitive data persists. Retest on physical device through lock and reboot boundaries. Mobile testing can review entitlements, runtime storage, and real device behavior.

Evidence retention

Keep anonymized review evidence: target name, build number, entitlement extract, item class, access group, file path category, device state, command or test step, observed result, owner, and retest date. Record pass only after expected lock-state behavior is seen on supported physical device. Record exception only with data owner approval, reason, expiry date, and alternative control. A migration requirement is not an excuse to widen availability without documented recovery risk.

Worked Keychain behavior and closure

Store a synthetic session item with explicit service, account namespace, accessibility class, and access-control requirement. Lock device, restart application, and attempt read before first unlock, after unlock, and after biometric policy change. Expected behavior must match selected class: kSecAttrAccessibleWhenUnlockedThisDeviceOnly should deny access while device is locked and should not migrate in backup. Record OSStatus result, not secret content, because platform returns distinguishable errors useful for diagnosis.

Edge case: uninstall does not always remove Keychain items. A newly installed application may observe prior item unless product explicitly clears or namespaces state during onboarding. Test account switch and restore path so old user material cannot attach to different account. Keychain protection is local storage control; backend token validation, expiration, and revocation remain necessary.

Close with build, entitlement extract, item attributes without value data, lock-state result, uninstall/reinstall result, recovery decision, and owner. Reopen after entitlement change, access group sharing, authentication redesign, or token format migration.

Sources

Have a system that needs testing?