敏感数据不能随便存
Token、密码、密钥这类敏感数据,写进 UserDefaults 或普通沙盒文件并不加密——一旦设备越狱或备份被取走就能读到。iOS 提供了钥匙串这一专门的加密存储,把这类东西放进它,才是正确姿势。
原则:敏感凭证进钥匙串,普通偏好进 UserDefaults。
这篇把钥匙串与数据保护讲清。
钥匙串 Keychain 是什么
钥匙串是系统级的加密数据库,数据用设备密钥加密存储,只有本 App 在解锁状态下能读写。它跨 App 卸载重装仍保留,适合存登录态。存取通过一组 Security 框架的接口。
能进钥匙串的数据
- 登录令牌会话凭证。
- 用户密码尽量不自存,交给系统钥匙串。
- 加密密钥本地数据的密钥。
存取一条钥匙串数据
典型流程:构造查询字典(服务名、账号),调用 SecItemAdd 写入,SecItemCopyMatching 读出,SecItemUpdate 更新,SecItemDelete 删除。
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "token",
kSecValueData as String: data
]
SecItemAdd(query as CFDictionary, nil)
读写都包一层小封装,业务代码不用每次写字典。
数据保护等级
写文件时可指定 Data Protection 等级:Complete(设备解锁后才可读)、CompleteUnlessOpen 等。等级决定文件在锁屏、重启后是否可访问。默认高等级即默认加密,选等级就是选解锁门槛。
隐私权限的正确姿势
访问相册、定位、麦克风都要在 Info.plist 写用途说明,且用到时才请求,不要启动时一股脑弹。系统会记录用户拒绝次数,滥用请求会降低信任。先说明为什么需要,再弹权限框。
实战:安全保存登录令牌
登录成功把令牌写进钥匙串,下次启动先从钥匙串读。
func saveToken(_ t: String) {
let d = Data(t.utf8)
SecItemDelete(baseQuery as CFDictionary)
SecItemAdd(baseQuery.merging([kSecValueData as String: d]) { $1 } as CFDictionary, nil)
}
登出时 SecItemDelete 清掉。登录态就此安全持久化,不落明文文件。
隐私清单与最小化采集
苹果要求声明 Privacy Manifest:采集了什么数据、用于什么目的。原则是最小化——不采集就别申请。把安全与隐私当成产品的一部分:用户把数据交给你,你得用对地方、存得安全。
- 敏感凭证进钥匙串。
- 权限用到才要,说明白用途。
- 数据采集按需,声明清楚。