Insecure Keychain Storage
不安全的 Keychain 访问控制
描述
Keychain 是一个专门用于在 iOS 设备上安全存储敏感数据的数据库。密码、加密密钥或证书等数据都可以保存在 Keychain 中。
在创建或更新 Keychain 项目时,开发人员可以使用 kSecAttrAccessible 属性来决定其可被访问的条件。使用弱访问控制值(如 kSecAttrAccessibleAlways 或 kSecAttrAccessibleAlwaysThisDeviceOnly)会导致即使在设备锁定状态下,Keychain 项目依然可以被访问。
由于安全风险,kSecAttrAccessibleAlways 和 kSecAttrAccessibleAlwaysThisDeviceOnly 已从 iOS 12.0 开始被弃用。使用它们可能导致在设备丢失、被盗或被入侵时,敏感数据暴露给未经授权的个体。
在以下示例中,由于未完整指定访问控制标志(SecAccessControlCreateFlags),导致 Keychain 项目的存储不安全:
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()
在这些示例中,正确指定访问控制标志以确保安全的 Keychain 项目存储至关重要。如果没有诸如 kSecAccessControlUserPresence 之类的特定标志,Keychain 项目仍容易受到未授权访问。
建议
当简单的访问控制要求已足够时,您可以直接使用 kSecAttrAccessible 指定访问级别。在这些情况下省略 kSecAttrAccessControl 是可以接受的。
| 场景 | 保护 |
|---|---|
| 存储始终可访问的数据 | kSecAttrAccessibleAlways |
| 存储首次解锁后可访问的数据 | kSecAttrAccessibleAfterFirstUnlock |
| 存储仅在设备解锁时可访问的数据 | kSecAttrAccessibleWhenUnlocked |
示例:
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()
对于更复杂的安全要求(例如需要用户在场或实施特定于应用程序的密码),请使用 SecAccessControlCreateWithFlags 创建带有附加标志的访问控制。
| 场景 | 保护 | 标志 |
|---|---|---|
| 处理需要用户在场的敏感数据 | kSecAttrAccessibleWhenUnlocked |
kSecAccessControlUserPresence |
| 处理需要特定密码以提供额外安全性的敏感数据 | kSecAttrAccessibleWhenPasscodeSet |
kSecAccessControlApplicationPassword |
示例:
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()
链接
标准
- 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
- CNIL_FOR_EDITORS:
- EDITORS_1_3_1
- HIPAA_CONTROLS:
- SECURITY251
- SECURITY212
- SECURITY213