Insecure Keychain Storage
Insecure Keychain Storage
Description
The platform relies on the keychain for storage, but it's configured with insecure flags.
In this example, the secret password is stored in the keychain with unrestricted accessibility (kSecAttrAccessibleAlways
), leaving it vulnerable to unauthorized access:
import Foundation
import Security
func saveSecretToKeychain() {
let secretData = "mySecretPassword".data(using: .utf8)!
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "myAccount",
kSecValueData as String: secretData,
kSecAttrAccessible as String: kSecAttrAccessibleAlways
]
SecItemAdd(query as CFDictionary, nil)
}
saveSecretToKeychain()
In the following examples, incomplete specification of access control flags (SecAccessControlCreateFlags
) results in insecure keychain item storage:
import Foundation
import Security
func saveSecretToKeychain() {
let secretData = "mySecretPassword".data(using: .utf8)!
let access = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleAlways,
.or,
nil
)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "myAccount",
kSecValueData as String: secretData,
kSecAttrAccessControl as String: access!,
]
SecItemAdd(query as CFDictionary, nil)
}
saveSecretToKeychain()
import Foundation
import Security
func saveSecretToKeychain() {
let secretData = "mySecretPassword".data(using: .utf8)!
let access = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleAlways,
.and,
nil
)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "myAccount",
kSecValueData as String: secretData,
kSecAttrAccessControl as String: access!,
]
SecItemAdd(query as CFDictionary, nil)
}
saveSecretToKeychain()
In these examples, it's critical to specify access control flags properly to ensure secure keychain item storage. Without specific flags like kSecAccessControlUserPresence, the keychain items remain vulnerable to unauthorized access.
Recommendation
When simple access control requirements suffice, you can directly specify the accessibility level using kSecAttrAccessible
. Omitting kSecAttrAccessControl
is acceptable in these cases.
Use Case | Protection |
---|---|
Storing data always accessible | kSecAttrAccessibleAlways |
Storing data accessible after first unlock | kSecAttrAccessibleAfterFirstUnlock |
Storing data only accessible when device unlocked | kSecAttrAccessibleWhenUnlocked |
Example:
import Foundation
import Security
func saveSecretToKeychain() {
let secretData = "mySecretPassword".data(using: .utf8)!
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "myAccount",
kSecValueData as String: secretData,
kSecAttrAccessible as String: kSecAttrAccessibleAlways
]
SecItemAdd(query as CFDictionary, nil)
}
saveSecretToKeychain()
For more complex security requirements, such as demanding user presence or implementing application-specific passwords, use SecAccessControlCreateWithFlags
to create access control with additional flags.
Use Case | Protection | Flags |
---|---|---|
Dealing with sensitive data that requires a user presence | kSecAttrAccessibleWhenUnlocked |
kSecAccessControlUserPresence |
Dealing with sensitive data that requires a specific password for extra security | kSecAttrAccessibleWhenPasscodeSet |
kSecAccessControlApplicationPassword |
Example:
import Foundation
import Security
func saveSecretToKeychain() {
let secretData = "mySecretPassword".data(using: .utf8)!
let access = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlocked,
kSecAccessControlUserPresence,
nil
)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "myAccount",
kSecValueData as String: secretData,
kSecAttrAccessControl as String: access!,
]
SecItemAdd(query as CFDictionary, nil)
}
saveSecretToKeychain()
Links
Standards
- OWASP_MASVS_L2:
- MSTG_AUTH_8
- GDPR:
- ART_5
- ART_25
- ART_32
- PCI_STANDARDS:
- REQ_6_2
- REQ_6_3
- REQ_8_3
- REQ_11_3
- SOC2_CONTROLS:
- CC_2_1
- CC_4_1
- CC_6_1
- CC_7_1
- CC_7_2
- CC_7_4
- CC_7_5