swift-security-expert
Audited by Runlayer on Mar 14, 2026
Risky tool definition detected
```swift // ❌ INCORRECT — .xcconfig value compiled into Info.plist as plaintext // In Secrets.xcconfig: MAPS_API_KEY = gm_pk_a1b2c3d4e5f6g7h8i9 // In Info.plist: <key>MapsAPIKey</key><string>$(MAPS_API_KEY)</string> let apiKey = Bundle.main.infoDictionary?["MapsAPIKey"] as? String // Attacker: unzip App.ipa && plutil -p Payload/App.app/Info.plist | grep Maps ``` ### Anti-Pattern 4 — Missing kSecAttrAccessible Specification When you add a Keychain item without specifying `kSecAttrAccessible`, the
> For complete accessibility constant selection criteria, data protection tier explanations, and `SecAccessControl` interaction rules, see `keychain-access-control.md` § The "When" Layer: Seven Accessibility Constants. --- ## Actor-Based KeychainManager — Thread-Safe Credential Storage The `SecItemAdd`, `SecItemCopyMatching`, `SecItemUpdate`, and `SecItemDelete` functions (all iOS 2.0+) are synchronous C functions performing IPC to the `securityd` daemon.
Risky tool definition detected
Risky tool definition detected
keychainRead( service: serviceName, account: timestampAccount), let str = String(data: data, encoding: .utf8), let migrationDate = ISO8601DateFormatter().date(from: str) else { return } let days = Calendar.current.dateComponents( [.day], from: migrationDate, to: Date()).day ?? 0 guard days >= cleanupDelayDays else { return } // Past rollback window — safe to permanently delete legacy files let documentsURL = FileManager.default.urls( for: .documentDirectory, in: .userDomainMask).first!
**Team ID changes sever all keychain access** — must release bridge update under old Team ID before app transfer; no recovery possible after transfer without bridge 11.
Risky tool definition detected
Correct configuration requires exact Team ID prefixes, per-target entitlements, and explicit `kSecAttrAccessGroup` usage in code — three requirements that most AI-generated code gets wrong. This reference covers access group mechanics, the two entitlement systems, correct and incorrect Swift patterns, macOS-specific requirements, iCloud sync, platform edge cases, and debugging strategies.
Synchronized items benefit from end-to-end encryption — Apple cannot decrypt the data. --- ## Cross-Target Entitlements Setup Extensions are separate sandboxed executable targets that do **not** inherit capabilities from their containing app. ### Xcode Configuration Steps 1.
Tool passed security scan
Risky tool definition detected
Tool: swift-security-expert/references/compliance-owasp-mapping.md [2/5] Description: Anti-pattern: common AI-generated credential storage ```swift // ❌ WRONG — UserDefaults writes to UNENCRYPTED plist at: // <AppSandbox>/Library/Preferences/<BundleID>.plist // Extractable via iTunes backup, iMazing, or objection UserDefaults.standard.set(apiToken, forKey: "auth_token") // ❌ WRONG — Hardcoded API key in source (found in 71% of iOS apps) let stripeKey = "sk_live_4eC39HqLyjWDarjtT1zdp7dc" // ❌ WRO
First, the Keychain is the universal compliance mechanism on iOS — a single correctly configured `SecItemAdd` with `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` and `.biometryCurrentSet` simultaneously satisfies M1, M3, and M9.
Risky tool definition detected
**Fetch metadata first** — if you only need to check existence, do not request `kSecReturnData` ### Envelope Encryption for Large Data For data beyond a few kilobytes, use the **DEK/KEK pattern**: store a 32-byte AES-256 Data Encryption Key (DEK) in the keychain, encrypt the actual data with that key, and write the ciphertext to a file protected by `NSFileProtection`. OWASP MASTG recommends this pattern for MASVS L2 compliance. Accessibility-to-file-protection mapping: - `kSecAttrAccessibleWhenU
**kSecUseDataProtectionKeychain on macOS** — Set to `true` for all operations to get iOS-identical behavior and avoid silent legacy keychain fallback 8.
Risky tool definition detected
Both must be satisfied for a read to succeed. Getting this wrong is the single most common cause of production keychain failures — background operations that silently return `nil`, items that vanish after device migration, or credentials left decryptable at rest. Sources: Apple Platform Security Guide (2024–2026 editions), Apple Keychain Services documentation, TN3137, WWDC 2014 Session 711 ("Keychain and Authentication with Touch ID"), WWDC 2015 Session 706, SecAccessControl documentation, OWAS
Tool passed security scan
Risky tool definition detected
Always target the data protection keychain on macOS.** Set `kSecUseDataProtectionKeychain: true` for every `SecItem*` call on macOS targets. Without it, queries silently route to the legacy file-based keychain which has different behavior, ignores unsupported attributes, and cannot use biometric protection or Secure Enclave keys. Mac Catalyst and iOS-on-Mac do this automatically.
Prompt Injection
Context Poisoning
Guardrail Bypass
Risky tool definition detected
### OWASP MASTG Keychain Validation MASTG-TEST-0052 requires that sensitive data use the Keychain, not `NSUserDefaults` or `.plist` files. OWASP also documents that keychain data persists after app uninstallation — the app sandbox is wiped but keychain items remain.
Tool passed security scan
Risky tool definition detected
Risky tool definition detected
--- ## The Secure Pattern — Hardware-Bound Secrets The correct architecture stores a secret in the iOS keychain with biometric access control. The secret's encryption key is held by the Secure Enclave — a dedicated processor running its own microkernel (sepOS), with its own encrypted memory, completely isolated from the application processor.
The app branches on a boolean in hookable memory. This is "trust the OS." **`evaluateAccessControl(_:operation:localizedReason:reply:)`** evaluates a `SecAccessControl` object for a specific cryptographic operation (`.useItem`, `.useKeySign`, `.useKeyDecrypt`). When used with keychain items, the authenticated `LAContext` is passed to `SecItemCopyMatching` via `kSecUseAuthenticationContext`, and the Secure Enclave recognizes the prior authentication.
Verification requires dynamic testing: **Test procedure:** On a jailbroken or instrumented device, inject a Frida script to hook `-[LAContext evaluatePolicy:localizedReason:reply:]` and force `success = true`. **Pass criteria:** The app prevents access to protected data despite the manipulated callback. The secret remains locked because `SecAccessControl` + Secure Enclave enforcement is independent of the boolean.
Risky tool definition detected
### ❌ Wrong: RSA when EC is available ```swift // Don't do this for new code — Security framework RSA let params: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeySizeInBits as String: 2048 ] var error: Unmanaged<CFError>? let key = SecKeyCreateRandomKey(params as CFDictionary, &error) // No type safety, manual memory management, 256-byte keys, no Secure Enclave ``` ### Preferred replacement: P256 signing in CryptoKit ```swift // ✅ CORRECT for new Apple-platform code l
Risky tool definition detected
Your app must detect `errSecItemNotFound` or authentication errors, explain to the user why re-authentication is needed, and generate a fresh key with server-side re-enrollment. (See `biometric-authentication.md` for full LAContext integration patterns.) --- ## Legacy Security framework approach (iOS 10+) Before CryptoKit, SE keys were created via `SecKeyCreateRandomKey` with `kSecAttrTokenIDSecureEnclave`.
Tool passed security scan
Risky tool definition detected
Risky tool definition detected