本节摘要:SOURCE 5.2:iOS 通过沙箱、Keychain、Data Protection、ATS、Secure Enclave、App Attest 构建封闭生态。本节与 Android 对照,突出 Keychain Accessible 级别与 Universal Links 防钓鱼。
Container 结构:Documents、Library/Caches、tmp。App 间默认不可互访;App Groups 仅在同团队扩展间共享,需 entitlement 审计。
| Accessible | 行为 |
|---|---|
| WhenUnlocked | 锁屏不可访问 |
| AfterFirstUnlock | 后台刷新可用 |
| WhenUnlockedThisDeviceOnly | 不备份、不迁移 |
Data Protection:NSFileProtectionComplete 文件级加密,锁屏密钥抹除。
// Keychain 存 refresh token kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
默认要求 HTTPS + 前向保密。NSExceptionDomains 仅在有充分理由时使用;App Store 审核会问。
LocalAuthentication 框架:LAContext.evaluatePolicy — 生物模板不出 Secure Enclave,App 只收 success/fail。
App Attest:服务端验证 Apple 签发的 attestation,识别模拟器/重打包客户端。

| 能力 | Android | iOS |
|---|---|---|
| 密钥硬件 | Keystore/StrongBox | Secure Enclave |
| 完整性 | Play Integrity | App Attest |
| 网络 | NSC | ATS |
| 侧载 | 多渠道 | 需企业签/越狱 |
⚠️ 常见坑:Keychain 项设
AfterFirstUnlock导致锁屏被窃仍可读 Token。
💡 关键直觉:iOS 封闭性降低恶意侧载,但 Jailbreak + Frida 仍可在研究环境 Hook。
下一节 SDLC 与 GDPR/个保法合规。
Keychain 的 Accessible 级别直接决定设备失窃后的风险。原则:会话类 secret 用 WhenUnlockedThisDeviceOnly,备份时不同步、换机不迁移;后台刷新需要的凭据用 AfterFirstUnlock,但要在锁屏后被读取时做额外校验。选错级别,等于把「锁屏也能读 Token」的钥匙交给攻击者。
// Keychain 保护级别选择(示意) let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kSecAttrAccount as String: "session_token", kSecValueData as String: tokenData ] SecItemAdd(query as CFDictionary, nil)
ATS 默认强制 HTTPS,例外域名要文档化并过审核;Universal Links 优于自定义 scheme——自定义 scheme 可以被其他 App 注册劫持,Universal Links 校验域名所有权,天然防钓鱼。App Attest 让服务端验证客户端真实性:客户端请求 attestation,服务端验签,识别模拟器与重打包客户端。生物识别用 LocalAuthentication,模板留在 Secure Enclave,App 只拿结果。
| 机制 | 作用 | 误用 |
|---|---|---|
| Keychain 级别 | 失窃风险控制 | AfterFirstUnlock 存 Token |
| ATS | 强制 HTTPS | 广开例外 |
| Universal Links | 防深链劫持 | 依赖自定义 scheme |
| App Attest | 客户端真实性 | 不做服务端验签 |
iOS 上线的安全清单与 Android 对照执行:Keychain 用 WhenUnlockedThisDeviceOnly 存会话、ATS 不放开例外(或例外文档化)、Universal Links 替代自定义 scheme、文件保护开启 NSFileProtectionComplete、App Attest 接入且服务端验签、发布包开启符号剥离。iOS 生态封闭性降低了侧载风险,但审计与测试仍不可省略——越狱设备上 Frida 一样能 hook,敏感逻辑不能指望「iOS 没人逆向」。
# iOS 上线前核对(示例) [ ] Keychain 级别 = ThisDeviceOnly [ ] ATS 无未文档化例外 [ ] Universal Links 已配置 [ ] 文件保护 = Complete [ ] App Attest 服务端验签 [ ] 发布包符号剥离
| 机制 | 启用方式 | 验证 |
|---|---|---|
| Keychain | 正确 Accessible 级别 | 锁屏后不可读 |
| ATS | 强制 HTTPS | 明文请求失败 |
| Universal Links | 域名所有权校验 | 深链不可劫持 |
| App Attest | 服务端验签 | 模拟器被拒 |
生物识别(Face ID/Touch ID)与 Keychain 配合,能实现「解锁后临时可用」的高强度保护,但要理解它的边界:App 拿到的只是 success/fail 结果,真正的验证在 Secure Enclave;生物识别可被系统层面的策略撤销(如连续失败锁定)。设备绑定(attestation)解决的是「客户端是否可信」——服务端验证 attestation 后,才能确认调用方是真实设备而非模拟器或重打包包。两者结合,是高价值业务(支付、转账)的标准配置,但也增加了流程复杂度,适合按风险分级启用而不是全功能无差别使用。