iOS Keychain 凭据安全存储
iOS Keychain 凭据安全存储
摘要:登录令牌、密钥和小型敏感凭据不适合保存在普通偏好设置中。Keychain 提供系统管理的加密凭据存储能力。本文介绍 Keychain 的适用范围、增删查改、访问属性与错误处理。
一、区分偏好设置与敏感凭据
UserDefaults 适合保存主题、开关和上次选中的页面等偏好信息,不应存放访问令牌、刷新令牌或私钥。Keychain 面向少量敏感数据,支持访问组、设备状态约束和系统级保护。
Keychain 不是通用数据库,不适合存储大量结构化记录、缓存图片或随手保存任意业务数据。应用仍要定义令牌过期、撤销、退出登录清理和设备迁移规则。
二、定义稳定的凭据标识
Keychain 项通常由服务名、账户名和访问组等属性定位。将这些标识集中定义,避免读取和删除时拼写不一致:
enum CredentialStore {
static let service = "app.session"
static let account = "refresh-token"
}
不要把令牌内容拼进服务名或账户名。标识用于查找项目,凭据本身作为受保护的数据保存。
三、保存数据
使用 Security 框架的查询字典添加数据:
func save(_ token: Data) throws {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: CredentialStore.service,
kSecAttrAccount as String: CredentialStore.account,
kSecValueData as String: token,
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
]
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError(status: status)
}
}
可访问性属性决定何时允许读取以及凭据是否可随备份迁移。后台需要访问的令牌和仅在设备解锁后使用的秘密,应采用不同策略。按数据敏感度和后台需求选择,而不是统一套用一种属性。
四、查询数据
查找时补充返回数据与返回数量要求:
func load() throws -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: CredentialStore.service,
kSecAttrAccount as String: CredentialStore.account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecItemNotFound { return nil }
guard status == errSecSuccess else { throw KeychainError(status: status) }
return result as? Data
}
“不存在”通常是正常业务状态,应与 Keychain 调用失败分开。调用成功后还要验证返回类型,避免不安全的强制转换。
五、更新与删除
更新使用匹配条件与属性字典调用 SecItemUpdate。若项目不存在,可按需求插入新项;这类“更新或插入”逻辑应显式处理并发调用。删除使用同一组稳定匹配属性调用 SecItemDelete,可将“已经不存在”视为幂等成功。
不要把更新失败一律当成插入。权限错误、设备锁定或参数错误可能需要暴露给上层,而不是被掩盖。
六、错误与并发封装
Security API 返回 OSStatus。封装层应保留状态码用于诊断,并转换为有限的业务错误类型。日志可以记录操作类型和状态分类,但不要打印密钥或令牌字节。
Keychain API 是同步调用。凭据数据通常很小,但封装边界应避免在高频 UI 渲染路径上反复读取;可在认证流程启动时读取一次,并将内存中的令牌生命周期控制在需要的范围。
七、访问控制与设备迁移
若凭据必须受 Face ID、Touch ID 或设备密码保护,可通过访问控制属性为读取增加用户验证条件。这样会影响后台刷新和自动登录流程,需针对实际体验设计。ThisDeviceOnly 类型的数据不会随设备备份迁移,适合绑定当前设备的秘密。
若启用 Keychain Access Group 在应用扩展间共享凭据,应缩小共享范围并检查签名配置。共享会扩大可访问主体,不能只因“以后可能用到”就启用。
八、测试与排查
使用唯一测试服务名隔离测试项。测试结束后删除测试凭据,避免不同用例相互污染。覆盖新增、读取、不存在、更新、删除和错误状态。设备重启、首次解锁前后及应用卸载重装行为,也应依据所选访问属性验证。
九、总结
Keychain 适合保存少量敏感凭据,正确使用还需要稳定的查询标识、合适的可访问性、完整的状态码处理和明确的退出清理。把系统存储细节封装在小型服务中,能让认证逻辑更清楚,也让安全策略更容易审查。
- 点赞
- 收藏
- 关注作者
评论(0)