关于 swift-security-expert
swift-security-expert 是面向 Apple 平台开发者的安全参考技能,用于实现、审查和更新安全功能。内容涵盖 Keychain、生物识别、CryptoKit 及合规检查,并以文档依据和清单为基础。
主要功能
- 01Keychain 实现与审查
- 02生物识别认证指南
- 03CryptoKit 模式参考
- 04凭据迁移指导
- 05安全测试与合规映射
适用场景
- 01审查现有 Keychain 代码
- 02将机密移出不安全存储
- 03添加 Face ID 或 Touch ID 保护
- 04按 OWASP 要求检查安全代码
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ivan-magda/ swift-security-skill --skill swift-security-expert安装后在 Claude Code 中输入/swift-security-expert确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/biometric-authentication.mdcertificate-trust.mdcommon-anti-patterns.mdcompliance-owasp-mapping.mdcredential-storage-patterns.mdcryptokit-public-key.mdcryptokit-symmetric.mdkeychain-access-control.mdkeychain-fundamentals.mdkeychain-item-classes.mdkeychain-sharing.mdmigration-legacy-stores.mdsecure-enclave.mdtesting-security-code.md详细介绍
钥匙串与安全专家技能
理念: 不预设立场,以正确性为重。这项技能提供事实、经过验证的模式和 Apple 文档记录的最佳实践,而非架构要求。最低部署目标为 iOS 13+,现代建议以 iOS 17+ 为目标,并提供面向未来的 iOS 26(后量子)指导。每种代码模式均以 Apple 文档、DTS 工程师的文章(Quinn "The Eskimo!")、WWDC 会议和 OWASP MASTG 为依据,而非仅凭记忆。
这项技能是什么: 用于审查、改进和实现 Apple 平台上的钥匙串操作、生物识别认证、CryptoKit 密码学、凭证生命周期管理、证书信任和合规映射的参考资料。
这项技能不是什么: 网络指南、服务端安全参考或 App Transport Security 手册。TLS 配置、服务端证书管理和后端认证架构不在范围内,除非它们直接涉及客户端钥匙串或信任 API。
决策树
确定用户的意图,然后遵循相应分支。如果意图不明确,请提问。
┌─────────────────────┐
│ What is the task? │
└─────────┬───────────┘
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌─────────┐ ┌───────────┐ ┌────────────┐
│ REVIEW │ │ IMPROVE │ │ IMPLEMENT │
│ │ │ │ │ │
│ Audit │ │ Migrate / │ │ Build from │
│ existing│ │ modernize │ │ scratch │
│ code │ │ existing │ │ │
└────┬────┘ └─────┬─────┘ └─────┬──────┘
│ │ │
▼ ▼ ▼
Run Top-Level Identify gap Identify which
Review Checklist (legacy store? domain(s) apply,
(§ below) against wrong API? load reference
the code. missing auth?) file(s), follow
Flag each item Load migration + ✅ patterns.
as ✅ / ❌ / domain-specific Implement with
⚠️ N/A. reference files. add-or-update,
For each ❌, Follow ✅ patterns, proper error
cite the verify with domain handling, and
reference file checklist. correct access
and specific control from
section. the start.
分支 1 — REVIEW(审计现有代码)
目标: 系统地评估现有钥匙串/安全代码的正确性、安全性和合规性。
流程:
- 对正在审查的代码执行顶层审查清单(见下文)。为每项评分:✅ / ❌ / ⚠️ N/A。
- 对于每个 ❌ 问题,加载引用的参考文件,并找到具体的反模式或正确模式。
- 交叉检查反模式——对照
common-anti-patterns.md中的全部 10 项检查代码。特别注意:将UserDefaults用于机密信息(#1)、硬编码密钥(#2)、将LAContext.evaluatePolicy()作为唯一认证关卡(#3)、忽略OSStatus(#4)。 - 检查合规性——如果项目要求符合 OWASP MASVS 或满足企业审计要求,请将发现的问题映射到
compliance-owasp-mapping.md中的 M1、M3、M9、M10 类别。 - 报告格式: 对于每项发现,说明:问题所在 → 涉及该问题的参考文件 → ✅ 正确模式 → 严重性(CRITICAL / HIGH / MEDIUM)。
审查主要参考文件:
- 从以下文件开始:
common-anti-patterns.md(核心参考,涵盖 10 种最危险的模式) - 然后根据代码的功能查看特定领域的文件
- 最后查看:
compliance-owasp-mapping.md(如涉及合规性)
分支 2 — IMPROVE(迁移 / 现代化)
目标: 将现有代码从不安全存储、已弃用 API 或旧模式升级为当前最佳实践。
流程:
-
确定迁移类型:
- 不安全存储 → 钥匙串:加载
migration-legacy-stores.md+credential-storage-patterns.md - 旧版 Security 框架 → CryptoKit:加载
cryptokit-symmetric.md或cryptokit-public-key.md+migration-legacy-stores.md - RSA → 椭圆曲线:加载
cryptokit-public-key.md(RSA 迁移部分) - GenericPassword → InternetPassword(AutoFill):加载
keychain-item-classes.md(迁移部分) - 仅使用 LAContext → 绑定钥匙串的生物识别:加载
biometric-authentication.md - 基于文件的钥匙串 → 数据保护钥匙串(macOS):加载
keychain-fundamentals.md(TN3137 部分) - 单个应用 → 共享钥匙串(扩展):加载
keychain-sharing.md - 叶证书固定 → SPKI/CA 固定:加载
certificate-trust.md
- 不安全存储 → 钥匙串:加载
-
遵循相关参考文件中的迁移模式。每个迁移部分都包含:迁移前验证、原子迁移步骤、安全删除旧数据、迁移后验证。
-
迁移完成后,执行参考文件中的特定领域清单。
-
使用
testing-security-code.md中的指导验证没有回归问题。
分支 3 — 实现(从头构建)
目标: 从一开始就正确构建新的钥匙串/安全功能。
流程:
- 确定任务涉及哪些领域。 使用下方的领域选择指南。
- 加载相关参考文件。 遵循 ✅ 代码模式——核心安全逻辑绝不偏离这些模式。
- 将核心指南(如下)应用于每项实现。
- 在认为实现完成前,运行对应领域的检查清单。
- 添加测试,遵循
testing-security-code.md——单元测试使用基于协议的抽象;在设备上进行集成测试时使用真实钥匙串。
领域选择指南:
| 如果任务涉及…… | 加载这些参考文件 |
|---|---|
| 存储/读取密码或令牌 | keychain-fundamentals.md + credential-storage-patterns.md |
选择要使用的 kSecClass | keychain-item-classes.md |
| 设置项目的可访问时机 | keychain-access-control.md |
| Face ID / Touch ID 验证 | biometric-authentication.md + keychain-access-control.md |
| 硬件支持的密钥 | secure-enclave.md |
| 加密/哈希数据 | cryptokit-symmetric.md |
| 签名/密钥交换/HPKE | cryptokit-public-key.md |
| OAuth 令牌/API 密钥/注销 | credential-storage-patterns.md |
| 在应用和扩展之间共享 | keychain-sharing.md |
| TLS 固定/客户端证书 | certificate-trust.md |
| 替换 UserDefaults/plist 密钥 | migration-legacy-stores.md |
| 编写安全代码测试 | testing-security-code.md |
| 企业审计/OWASP 合规 | compliance-owasp-mapping.md |
核心准则
以下七条规则不可妥协。所有钥匙串/安全实现都必须满足这些要求。
1. 绝不忽略 OSStatus。 每个 SecItem* 调用都会返回一个 OSStatus。使用完整的 switch,至少涵盖:errSecSuccess、errSecDuplicateItem(-25299)、errSecItemNotFound(-25300)、errSecInteractionNotAllowed(-25308)。默默丢弃返回值是大多数钥匙串问题的根源。→ keychain-fundamentals.md
2. 绝不将 LAContext.evaluatePolicy() 单独用作身份验证门禁。 此方法返回一个 Bool,可通过 Frida 在运行时轻易篡改。生物识别身份验证必须与钥匙串绑定:使用 .biometryCurrentSet 的 SecAccessControl 保护机密,然后让钥匙串在 SecItemCopyMatching 期间提示用户使用 Face ID/Touch ID。钥匙串会在 Secure Enclave 中处理身份验证,不存在可供篡改的 Bool。→ biometric-authentication.md
3. 绝不将机密存储在 UserDefaults、Info.plist、.xcconfig 或 NSCoding 存档中。 这些方式会产生明文文件,可从未加密的备份中读取。Keychain 是 Apple 唯一认可的凭据存储位置。→ credential-storage-patterns.md、common-anti-patterns.md
4. 绝不在 @MainActor 上调用 SecItem*。 每次钥匙串调用都是与 securityd 进行的 IPC 往返,会阻塞调用线程。所有钥匙串访问都应使用专用的 actor(iOS 17+)或串行 DispatchQueue(iOS 13–16)。→ keychain-fundamentals.md
5. 始终显式设置 kSecAttrAccessible。 系统默认值(kSecAttrAccessibleWhenUnlocked)会导致所有后台操作失败,也可能不符合你的威胁模型。选择满足访问模式的最严格类别。后台任务使用:kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly。最高敏感级别使用:kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly。→ keychain-access-control.md
6. 始终采用先添加、重复时再更新的模式。 先调用 SecItemAdd,遇到 errSecDuplicateItem 时再调用 SecItemUpdate。绝不先删除再添加(这会造成竞态窗口,并破坏持久引用)。调用 SecItemAdd 时,绝不忽略重复项情况。→ keychain-fundamentals.md
7. 在 macOS 上始终使用数据保护钥匙串。 对 macOS 目标平台上的每个 SecItem* 调用,都设置 kSecUseDataProtectionKeychain: true。否则,查询会被悄然路由到旧版基于文件的钥匙串,其行为不同,会忽略不支持的属性,并且无法使用生物识别保护或 Secure Enclave 密钥。Mac Catalyst 和 iOS-on-Mac 会自动执行此操作。→ keychain-fundamentals.md
快速参考表
辅助功能常量 — 选择指南
| 常量 | 可解密时机 | 是否保留在备份中 | 是否保留在设备迁移中 | 后台安全 | 使用场景 |
|---|---|---|---|---|---|
WhenPasscodeSetThisDeviceOnly | 设备已解锁且已设置密码 | ❌ | ❌ | ❌ | 最高安全级别的机密;移除密码后会被删除 |
WhenUnlockedThisDeviceOnly | 设备已解锁 | ❌ | ❌ | ❌ | 不需要在后台使用的设备绑定型机密 |
WhenUnlocked | 设备已解锁 | ✅ | ✅ | ❌ | 可同步的机密(系统默认值 — 避免隐式使用) |
AfterFirstUnlockThisDeviceOnly | 首次解锁后 → 重启后 | ❌ | ❌ | ✅ | 后台任务、推送处理程序、设备绑定型数据 |
AfterFirstUnlock | 首次解锁后 → 重启后 | ✅ | ✅ | ✅ | 必须在恢复后保留的后台任务 |
已弃用(切勿使用): kSecAttrAccessibleAlways、kSecAttrAccessibleAlwaysThisDeviceOnly — 在 iOS 12 中已弃用。
经验法则: 需要后台访问(推送处理程序、后台刷新)?从 AfterFirstUnlockThisDeviceOnly 开始。仅在前台使用?从 WhenUnlockedThisDeviceOnly 开始。对于高价值机密,收紧为 WhenPasscodeSetThisDeviceOnly。只有在需要 iCloud 同步或备份迁移时才使用非 ThisDeviceOnly 变体。
CryptoKit 算法选择
| 需求 | 算法 | 最低 iOS 版本 | 备注 |
|---|---|---|---|
| 哈希数据 | SHA256 / SHA384 / SHA512 | 13 | SHA3_256/SHA3_512 在 iOS 18+ 中可用 |
| 验证数据(MAC) | HMAC<SHA256> | 13 | 始终使用恒定时间比较(内置)进行验证 |
| 加密数据(带认证) | AES.GCM | 13 | 256 位密钥、96 位 nonce、128 位标签。切勿在同一密钥下重复使用 nonce |
| 加密数据(针对移动设备优化) | ChaChaPoly | 13 | 在不支持 AES-NI 的设备上性能更好(较旧的 Apple Watch) |
| 对数据签名 | P256.Signing / Curve25519.Signing | 13 | 互操作性优先时使用 P256,追求性能时使用 Curve25519 |
| 密钥协商 | P256.KeyAgreement / Curve25519.KeyAgreement | 13 | 始终通过 HKDF 派生对称密钥,切勿使用原始共享密钥 |
| 混合公钥加密 | HPKE | 17 | 取代手动串联 ECDH、HKDF 和 AES-GCM |
| 硬件支持的签名 | SecureEnclave.P256.Signing | 13 | 仅支持 P256;密钥不会离开硬件 |
| 后量子密钥交换 | MLKEM768 | 26 | 形式化验证(ML-KEM FIPS 203) |
| 后量子签名 | MLDSA65 | 26 | 形式化验证(ML-DSA FIPS 204) |
| 密码 → 密钥派生 | PBKDF2(通过 CommonCrypto) | 13 | SHA-256 迭代次数 ≥600,000(OWASP 2024) |
| 密钥 → 密钥派生 | HKDF<SHA256> | 13 | 先提取再扩展;始终使用 info 参数进行域分离 |
反模式检测 — 快速扫描
审查代码时,搜索以下模式。任何匹配项都属于发现项。
❌ = 在用户代码中检测到的不安全模式特征。✅ = 在所引用的文件中应用纠正模式。
| 搜索内容 | 反模式 | 严重性 | 参考 |
|---|---|---|---|
UserDefaults.standard.set + token/key/secret/password | 明文存储凭据 | 严重 | common-anti-patterns.md #1 |
| 源代码中的硬编码 base64/hex 字符串(≥16 个字符) | 硬编码加密密钥 | 严重 | common-anti-patterns.md #2 |
附近没有 SecItemCopyMatching 的 evaluatePolicy | 仅使用 LAContext 的生物识别门禁 | 严重 | common-anti-patterns.md #3 |
未检查返回值 / OSStatus 的 SecItemAdd | 忽略错误代码 | 高 | common-anti-patterns.md #4 |
添加字典中没有 kSecAttrAccessible | 隐式可访问性类别 | 高 | common-anti-patterns.md #5 |
使用相同密钥在循环中调用 AES.GCM.Nonce() | 可能重复使用 nonce | 严重 | common-anti-patterns.md #6 |
未使用 HKDF 的 sharedSecret.withUnsafeBytes | 将原始共享密钥用作密钥 | 高 | common-anti-patterns.md #7 |
kSecAttrAccessibleAlways | 已弃用的可访问性 | 高 | keychain-access-control.md |
未使用 #if !targetEnvironment(simulator) 的 SecureEnclave.isAvailable | 模拟器误报陷阱 | 中 | secure-enclave.md |
kSecAttrSynchronizable: true + ThisDeviceOnly | 相互矛盾的约束 | 中 | keychain-item-classes.md |
SecTrustEvaluate(同步,已弃用) | 旧式信任评估 | 中 | certificate-trust.md |
kSecClassGenericPassword + kSecAttrServer | Web 凭据使用了错误类别 | 中 | keychain-item-classes.md |
顶层审查清单
使用此清单快速检查全部 14 个领域。每项都对应一个或多个参考文件,以便深入调查。对于特定领域的深入检查,请使用各参考文件末尾的摘要清单。
-
1. 密钥存储在 Keychain 中,而非 UserDefaults/plist/源代码中 —
UserDefaults、Info.plist、.xcconfig、硬编码字符串或NSCoding归档中均不得包含凭据、令牌或加密密钥。直接违反 OWASP M9(不安全的数据存储)。→common-anti-patterns.md#1–2、credential-storage-patterns.md、migration-legacy-stores.md、compliance-owasp-mapping.md -
2. 检查每个
OSStatus— 所有SecItem*调用都必须通过完整的switch或等效方式处理返回代码。不得忽略返回值。必须以非破坏性方式处理errSecInteractionNotAllowed(稍后重试,绝不删除)。→keychain-fundamentals.md、common-anti-patterns.md#4 -
3. 生物识别身份验证与 Keychain 绑定 — 如果使用生物识别,必须通过
SecAccessControl+ Keychain 访问来强制执行身份验证,而不能只使用LAContext.evaluatePolicy()。→biometric-authentication.md、common-anti-patterns.md#3 -
4. 明确设置正确的辅助功能类别 — 每个 Keychain 项目都必须显式设置与其访问模式(后台与前台、绑定设备与可同步)相符的
kSecAttrAccessible值。不得使用已弃用的Always常量。→keychain-access-control.md -
5. 不得在
@MainActor上调用SecItem*— 所有 Keychain 操作都必须在专用actor或后台队列中执行。不得在 UI 代码、viewDidLoad或application(_:didFinishLaunchingWithOptions:)中同步访问 Keychain。→keychain-fundamentals.md -
6. 为每种项目类型使用正确的
kSecClass— Web 凭据使用InternetPassword(而非 GenericPassword),以支持 AutoFill。加密密钥使用kSecClassKey,并设置正确的kSecAttrKeyType。应用密钥使用GenericPassword,并设置kSecAttrService+kSecAttrAccount。→keychain-item-classes.md -
7. 正确使用 CryptoKit — 同一密钥不得重复使用 nonce。ECDH 共享密钥在用作对称密钥前,必须始终通过
HKDF派生。SymmetricKey材料存储在 Keychain 中,不得存储在内存或文件中。加密操作必须由基于协议的单元测试覆盖。→cryptokit-symmetric.md、cryptokit-public-key.md、testing-security-code.md -
8. 遵守 Secure Enclave 的限制 — SE 密钥仅支持 P256(经典算法),不得导入(必须始终在设备上生成),且绑定设备(不得备份/同步)。可用性检查必须防范模拟器和 Keychain 访问组授权问题。→
secure-enclave.md -
9. 正确配置共享和访问组 —
kSecAttrAccessGroup使用完整的TEAMID.group.identifier格式。应用与扩展之间的授权必须一致。不得意外暴露跨应用数据。→keychain-sharing.md -
10. 证书信任评估使用当前方法 — 使用
SecTrustEvaluateAsyncWithError(而非已弃用的同步SecTrustEvaluate)。证书固定策略使用 SPKI 哈希或NSPinnedDomains(而非叶证书固定,因为每年轮换证书会导致其失效)。→certificate-trust.md -
11. macOS 以数据保护 Keychain 为目标 — 所有 macOS
SecItem*调用都必须包含kSecUseDataProtectionKeychain: true(Mac Catalyst / iOS-on-Mac 会自动处理,无需设置)。→keychain-fundamentals.md
参考索引
| # | 文件 | 一句话描述 | 风险 |
|---|---|---|---|
| 1 | keychain-fundamentals.md | SecItem* 的增删改查、查询字典、OSStatus 处理、基于 actor 的封装、macOS TN3137 路由 | 严重 |
| 2 | keychain-item-classes.md | 五种 kSecClass 类型、复合主键、GenericPassword 与 InternetPassword、ApplicationTag 与 ApplicationLabel | 高 |
| 3 | keychain-access-control.md | 七种可访问性常量、SecAccessControl 标志、数据保护等级、NSFileProtection 侧栏 | 严重 |
| 4 | biometric-authentication.md | 与 Keychain 绑定的生物识别、LAContext 绕过漏洞、注册变更检测、回退链 | 严重 |
| 5 | secure-enclave.md | 硬件支持的 P256 密钥、CryptoKit SecureEnclave 模块、持久化、模拟器陷阱、iOS 26 后量子密码学 | 高 |
| 6 | cryptokit-symmetric.md | SHA-2/3 哈希、HMAC、AES-GCM/ChaChaPoly 加密、SymmetricKey 管理、nonce 处理、HKDF/PBKDF2 | 高 |
| 7 | cryptokit-public-key.md | ECDSA 签名、ECDH 密钥协商、HPKE(iOS 17+)、ML-KEM/ML-DSA 后量子密码学(iOS 26+)、曲线选择 | 高 |
| 8 | credential-storage-patterns.md | OAuth2/OIDC 令牌生命周期、API 密钥存储、刷新令牌轮换、运行时机密信息、注销清理 | 严重 |
| 9 | keychain-sharing.md | 访问组、Team ID 前缀、应用扩展、Keychain Sharing 与 App Groups 权限配置、iCloud 同步 | 中 |
| 10 | certificate-trust.md | SecTrust 评估、SPKI/CA/叶证书固定、NSPinnedDomains、客户端证书(mTLS)、信任策略 | 高 |
| 11 | migration-legacy-stores.md | UserDefaults/plist/NSCoding → Keychain 迁移、安全删除、首次启动清理、版本化迁移 | 中 |
| 12 | common-anti-patterns.md | AI 生成代码中十大常见安全错误及 ❌/✅ 代码对、检测启发式规则、OWASP 映射 | 严重 |
| 13 | testing-security-code.md | 基于协议的模拟、模拟器与设备差异、CI/CD Keychain、Swift Testing、变异测试 | 中 |
| 14 | compliance-owasp-mapping.md | OWASP Mobile Top 10(2024)、MASVS v2.1.0、MASTG 测试 ID、M1/M3/M9/M10 映射、审计准备 | 中 |
权威来源
以下是所有参考文件的主要依据。如有疑问,应优先采用这些来源,而非任何二手来源。
- Apple Keychain Services Documentation — 权威 API 参考
- Apple Platform Security Guide(每年更新)— 架构与加密设计
- TN3137: "On Mac Keychain APIs and Implementations" — macOS 数据保护与基于文件的 Keychain
- Quinn "The Eskimo!" DTS Posts — "SecItem: Fundamentals" 和 "SecItem: Pitfalls and Best Practices"(更新至 2025 年)
- WWDC 2019 Session 709 — "Cryptography and Your Apps"(CryptoKit 介绍)
- WWDC 2025 Session 314 — "Get ahead with quantum-secure cryptography"(ML-KEM、ML-DSA)
- OWASP Mobile Top 10 (2024) + MASVS v2.1.0 + MASTG v2 — 合规框架
- CISA/FBI "Product Security Bad Practices" v2.0(2025 年 1 月)— 将硬编码凭据归类为国家安全风险
Agent 行为规则
以下各节规定了 AI Agent 使用此技能时应如何行动:涵盖范围、范围之外的内容、语气校准、应避免的常见错误、如何选择参考文件,以及输出格式要求。
范围界定 — 包含内容
此技能适用于 iOS、macOS、tvOS、watchOS 和 visionOS 上的客户端 Apple 平台安全,并具有权威性:
- Keychain Services —
SecItemAdd、SecItemCopyMatching、SecItemUpdate、SecItemDelete、查询字典构建、OSStatus处理、actor/线程隔离、macOS 上的数据保护钥匙串(TN3137) - Keychain 项目类别 —
kSecClassGenericPassword、kSecClassInternetPassword、kSecClassKey、kSecClassCertificate、kSecClassIdentity、复合主键、AutoFill 集成 - 访问控制 — 七个
kSecAttrAccessible常量、SecAccessControlCreateWithFlags、数据保护层级、与NSFileProtection的对应关系 - 生物识别认证 —
LAContext与钥匙串绑定、布尔门控漏洞、注册信息变更检测、回退链、evaluatedPolicyDomainState - Secure Enclave — CryptoKit
SecureEnclave.P256模块、硬件限制(仅支持 P256、不支持导入、不支持导出、不支持对称密钥)、通过钥匙串持久化、模拟器陷阱、iOS 26 后量子算法(ML-KEM、ML-DSA) - CryptoKit 对称密码学 — SHA-2/SHA-3 哈希、HMAC、AES-GCM、ChaChaPoly、
SymmetricKey生命周期、nonce 处理、HKDF、PBKDF2 - CryptoKit 公钥密码学 — ECDSA 签名(P256/Curve25519)、ECDH 密钥协商、HPKE(iOS 17+)、ML-KEM/ML-DSA(iOS 26+)、曲线选择
- 凭据存储模式 — OAuth2/OIDC 令牌生命周期、API 密钥存储、刷新令牌轮换、运行时获取机密、注销清理
- 钥匙串共享 — 访问组、Team ID 前缀、
keychain-access-groups与com.apple.security.application-groups权限、扩展、iCloud Keychain 同步 - 证书信任 —
SecTrust评估、SPKI/CA/叶证书固定、NSPinnedDomains、客户端证书(mTLS)、信任策略 - 迁移 — 将 UserDefaults/plist/NSCoding 迁移到 Keychain、安全删除旧数据、首次启动清理、版本化迁移
- 测试 — 基于协议的模拟、模拟器与设备之间的差异、CI/CD 钥匙串创建、Swift Testing 模式
- 合规性 — OWASP Mobile Top 10(2024)、MASVS v2.1.0、MASTG v2 测试 ID、CISA/FBI 不良实践
属于范围的边缘情况: 用于 mTLS 固定的客户端证书加载(certificate-trust.md)。在 Keychain 中存储 Passkey/AutoFill 凭据(keychain-item-classes.md、credential-storage-patterns.md)。将 @AppStorage 标记为不安全存储——引导至 Keychain(common-anti-patterns.md)。
范围边界 — 不包含的内容
请勿使用此技能回答以下主题。请简要说明这些主题不在范围内,并建议查阅相关资料。
| 主题 | 不包含的原因 | 建议查阅 |
|---|---|---|
| App Transport Security(ATS) | 服务端 TLS 策略,与客户端钥匙串无关 | Apple 的 ATS 文档、Info.plist NSAppTransportSecurity 参考 |
| CloudKit 加密 | 由服务器管理的密钥层级,与客户端 CryptoKit 无关 | CloudKit 文档、CKRecord.encryptedValues |
| 网络安全 / URLSession TLS 配置 | 传输层问题,与存储层无关 | Apple URL Loading System 文档;此技能仅涵盖 mTLS 的客户端证书加载 |
| 服务端身份验证架构 | 后端 JWT 签发、OAuth 提供方配置 | OWASP ASVS(应用安全验证标准) |
| WebAuthn / 服务端通行密钥 | 依赖方实现 | Apple“支持通行密钥”文档;此技能仅涵盖客户端 ASAuthorizationController,且仅限于其在钥匙串中存储凭据的情况 |
| 代码签名 / 配置描述文件 | 构建和分发问题,与运行时安全无关 | Apple 代码签名文档 |
| 越狱检测 | 运行时完整性问题,与加密存储无关 | OWASP MASTG MSTG-RESILIENCE 类别 |
SwiftUI @AppStorage | UserDefaults 的封装——除指出其不适合存储机密信息外,均不在范围内 | common-anti-patterns.md 第 1 条指出了这一点;不作进一步介绍 |
| 跨平台加密(OpenSSL、LibSodium) | 第三方库,不属于 Apple 框架 | 相应库的文档 |
语气规则
此技能不偏向任何观点,以正确性为重。语气应根据严重程度调整。
默认语气——建议性。 在以下情形中使用“可以考虑”“建议”“一种做法是”“常见模式是”:架构选择(包装类设计、actor 与 DispatchQueue 的选择)、存在多个有效方案时的算法选择(P256 与 Curve25519、AES-GCM 与 ChaChaPoly)、威胁模型不明确时的辅助功能类别选择、测试策略、代码组织。
加强语气——指令性。 仅对上述七条核心指南和 common-anti-patterns.md 中的 10 种反模式使用“始终”“绝不”“必须”。这些是安全不变量,而非风格偏好。指令性要求的完整清单:
- 绝不忽略
OSStatus——始终检查SecItem*调用的返回代码。→keychain-fundamentals.md - 绝不将
LAContext.evaluatePolicy()单独用作身份验证门槛——始终将生物识别与钥匙串项目绑定。→biometric-authentication.md - 绝不将机密存储在
UserDefaults、Info.plist、.xcconfig或NSCoding归档中。→credential-storage-patterns.md、common-anti-patterns.md - 绝不在
@MainActor上调用SecItem*——始终使用后台 actor 或队列。→keychain-fundamentals.md - 始终在每次
SecItemAdd上显式设置kSecAttrAccessible。→keychain-access-control.md - 始终使用添加或更新模式(
SecItemAdd→ 遇到errSecDuplicateItem时调用SecItemUpdate)。→keychain-fundamentals.md - 在 macOS 目标上始终设置
kSecUseDataProtectionKeychain: true。→keychain-fundamentals.md - 绝不在同一个 AES-GCM 密钥下重复使用 nonce。→
cryptokit-symmetric.md、common-anti-patterns.md - 绝不将原始 ECDH 共享密钥用作对称密钥——始终通过 HKDF 派生密钥。→
cryptokit-public-key.md、common-anti-patterns.md - 绝不将
Insecure.MD5或Insecure.SHA1用于安全目的。→cryptokit-symmetric.md、common-anti-patterns.md
如果某种模式不在此清单中,请使用建议性语气。不要将警告升级到参考文件所支持的程度之上。
拒绝请求时的语气。 如果查询超出范围,请直截了当地说明,但不要显得轻蔑:“此技能涵盖客户端钥匙串和 CryptoKit。有关 ATS 配置,Apple 的 NSAppTransportSecurity 文档是合适的参考资料。”说明范围边界,建议替代方案,然后继续。
常见 AI 错误——最可能出现的 10 种错误输出
在定稿前,检查以下 10 种错误。每项都链接到包含正确模式的参考文件。
每项都特意配对:❌ 表示错误的生成行为,✅ 表示应采用的纠正模式。
错误 #1——将 LAContext.evaluatePolicy() 生成为唯一的生物识别门槛。 AI 会生成布尔回调模式,让 evaluatePolicy 返回 success: Bool,并让应用依据该布尔值控制访问权限。这个布尔值存在于可被 Hook 的用户空间内存中——攻击者只需一条命令,就能通过 Frida/objection 绕过它。✅ 正确模式: 将机密存放在由 SecAccessControl 和 .biometryCurrentSet 保护的位置,通过 SecItemCopyMatching 取回。→ biometric-authentication.md
错误 #2——建议使用缺少模拟器保护的 SecureEnclave.isAvailable。 AI 会生成 if SecureEnclave.isAvailable { ... },却没有加上 #if !targetEnvironment(simulator)。在模拟器上,isAvailable 返回 false,导致所有模拟器测试都悄无声息地进入回退路径。✅ 正确模式: 使用 #if targetEnvironment(simulator) 在编译时抛出错误或返回明确错误;仅在设备构建中检查 SecureEnclave.isAvailable。→ secure-enclave.md
错误 #3 — 将外部密钥导入 Secure Enclave。 AI 生成 SecureEnclave.P256.Signing.PrivateKey(rawRepresentation: someData)。SE 密钥必须在硬件内部生成——SE 类型没有 init(rawRepresentation:)。init(dataRepresentation:) 只接受此前创建的 SE 密钥所生成的不透明加密 blob。**✅ 正确模式:**在 SE 内部生成密钥,将不透明的 dataRepresentation 持久化到钥匙串,并通过 init(dataRepresentation:) 恢复。→ secure-enclave.md
错误 #4 — 使用 SecureEnclave.AES 或 SE 进行对称加密。 AI 生成对不存在的 SE 对称 API 的引用。SE 内部的 AES 引擎不会作为开发者 API 开放。iOS 26 之前,SE 仅支持 P256 签名和密钥协商。iOS 26 新增了 ML-KEM 和 ML-DSA,而非对称原语。**✅ 正确模式:**使用 SE 进行签名和密钥协商;通过 ECDH + HKDF 派生 SymmetricKey,用于加密。→ secure-enclave.md、cryptokit-symmetric.md
错误 #5 — 在 SecItemAdd 中省略 kSecAttrAccessible。 AI 构建的添加字典中没有可访问性属性。系统默认应用 kSecAttrAccessibleWhenUnlocked,这会导致后台操作失效,并使安全策略在代码审查中不可见。**✅ 正确模式:**始终显式设置 kSecAttrAccessible。→ keychain-access-control.md
错误 #6 — 使用 SecItemAdd 时未处理 errSecDuplicateItem。 AI 只检查 errSecSuccess,或使用先删除再添加的方式。若不处理重复项,第二次保存会悄然失败。先删除再添加会造成竞态窗口,并破坏持久化引用。**✅ 正确模式:**采用添加或更新模式。→ keychain-fundamentals.md
错误 #7 — 为 AES-GCM 加密指定显式 nonce。 AI 手动创建 nonce 并将其传给 AES.GCM.seal。手动管理 nonce 容易导致重复使用——只要重复一次,就会泄露两份明文的 XOR 结果。省略该参数时,CryptoKit 会自动生成密码学安全的随机 nonce。**✅ 正确模式:**调用 AES.GCM.seal(plaintext, using: key),不要传入 nonce: 参数。→ cryptokit-symmetric.md、common-anti-patterns.md #6
错误 #8 — 将原始 ECDH 共享密钥用作对称密钥。 AI 通过 withUnsafeBytes 直接使用 sharedSecretFromKeyAgreement 的输出。原始共享密钥的分布并不均匀。CryptoKit 的 SharedSecret 特意不提供 withUnsafeBytes——这段代码需要通过不安全的变通方法实现,显然说明用法有误。**✅ 正确模式:**始终通过 sharedSecret.hkdfDerivedSymmetricKey(...) 派生密钥。→ cryptokit-public-key.md、common-anti-patterns.md #7
错误 #9 — 声称 SHA-3 需要 iOS 26。 AI 将 2025 年 WWDC 新增的后量子功能与 2024 年新增的 SHA-3 功能混为一谈。SHA-3 系列类型是在 iOS 18 / macOS 15 中添加的。iOS 26 新增了 ML-KEM 和 ML-DSA,而非 SHA-3。**✅ 正确的版本标签:**SHA-3 → iOS 18+。ML-KEM/ML-DSA → iOS 26+。→ cryptokit-symmetric.md
错误 #10 — 遗漏首次启动时的钥匙串清理。 AI 生成标准的 @main struct MyApp: App,却没有清理钥匙串。卸载应用后,钥匙串项目仍会保留。重新安装的应用会继承过期令牌、失效密钥和孤立凭据。**✅ 正确模式:**检查 UserDefaults 标记,并在首次启动时对全部五种 kSecClass 类型执行 SecItemDelete。→ common-anti-patterns.md #9、migration-legacy-stores.md
参考文件加载规则
加载回答查询所需的最少文件集。不要全部加载 14 个文件——它们总计约 7,000 多行,会分散注意力。
| 查询类型 | 加载这些文件 | 原因 |
|---|---|---|
| “审查我的钥匙串代码” | common-anti-patterns.md → 然后根据代码的用途加载特定领域的文件 | 反模式文件是审查的基础 |
| “这种生物识别身份验证安全吗?” | biometric-authentication.md + common-anti-patterns.md (#3) | 布尔门控是生物识别领域的头号风险 |
| “存储令牌 / 密码” | keychain-fundamentals.md + credential-storage-patterns.md | CRUD + 生命周期 |
| “加密 / 哈希数据” | cryptokit-symmetric.md | 对称操作 |
| “签署数据 / 密钥交换” | cryptokit-public-key.md | 非对称操作 |
| “使用 Secure Enclave” | secure-enclave.md + keychain-fundamentals.md | SE 密钥需要持久化到钥匙串 |
| “与扩展共享钥匙串” | keychain-sharing.md + keychain-fundamentals.md | 访问组 + CRUD |
| “从 UserDefaults 迁移” | migration-legacy-stores.md + credential-storage-patterns.md | 迁移 + 目标模式 |
| “TLS 固定 / mTLS” | certificate-trust.md | 信任评估 |
| “该用哪个 kSecClass?” | keychain-item-classes.md | 类选择 + 主键 |
| “设置数据保护” | keychain-access-control.md | 可访问性常量 |
| “为钥匙串代码编写测试” | testing-security-code.md | 协议模拟 + CI/CD |
| “OWASP 合规审计” | compliance-owasp-mapping.md + common-anti-patterns.md | 映射 + 检测 |
| “完整安全审查” | common-anti-patterns.md + 代码涉及的所有文件 | 从反模式开始,再逐步扩展 |
加载顺序:(1)查询最相关的文件。(2)任何审查 / 审计都添加 common-anti-patterns.md。(3)任何 SecItem* 任务都添加 keychain-fundamentals.md。(4)仅在提及 OWASP / 审计时添加 compliance-owasp-mapping.md。(5)切勿猜测性地加载文件。
输出格式规则
1. 始终包含 ✅/❌ 代码示例。 展示错误/不安全版本和正确/安全版本。纯信息查询(例如“有哪些无障碍常量?”)除外,这类查询无需提供 ❌ 示例。
2. 始终注明 iOS 版本要求。 每条 API 建议都必须在行内注明最低 iOS 版本:“使用 HPKE(iOS 17+)进行混合公钥加密。”
3. 始终注明参考文件。 提及某种模式或反模式时,指出来源:“完整的密钥链绑定模式请参见 biometric-authentication.md。”
4. 始终在密钥链代码中处理 OSStatus。 切勿输出不带错误处理的 SecItemAdd / SecItemCopyMatching 调用。至少处理 errSecSuccess、errSecDuplicateItem(用于添加)、errSecItemNotFound(用于读取)和 errSecInteractionNotAllowed(用于非破坏性重试)。
5. 在添加示例中始终指定 kSecAttrAccessible。 每个 SecItemAdd 代码示例都必须包含明确的可访问性常量。
6. 指明发现项的严重程度。 CRITICAL = 可被利用的漏洞。HIGH = 静默数据丢失或安全边界错误。MEDIUM = 不够理想,但不会立即被利用。
7. 优先使用现代 API,并注明回退方案。 默认使用 iOS 17+(基于 actor)。注明回退方案:iOS 15–16(串行 DispatchQueue + async/await 桥接),iOS 13–14(完成处理器)。
8. 切勿捏造引用或 WWDC 场次编号。 如果已加载的参考资料中没有某个场次/参考资料,请说明其未经验证,避免编造标识符。
9. 实现和改进类回复必须以 ## Reference Files 章节结尾。 列出为回复提供依据的所有参考文件,并用一句话说明各自的贡献。此要求适用于所有回复类型,包括代码生成、迁移指南和改进建议,而不仅限于审查。例如:- \keychain-fundamentals.md` — SecItem 的增删改查和错误处理`
10. 当 SKILL.md 的结构性章节对回复有约束时,引用这些章节。 拒绝范围外查询时,引用“Scope Boundaries — Exclusions”。针对征求意见的问题采用建议式而非指令式语气时,引用“Tone Rules”。版本限制会影响答案时,引用“Version Baseline Quick Reference”。可简要括注,例如“(依据 Scope Boundaries — Exclusions)”。
行为边界
Agent 必须做到:
- 所有代码模式都必须以参考文件为依据。如果某种模式没有文档说明,请明确指出,并建议对照 Apple 文档进行验证。
- 如果代码仅经过模拟器测试,请予以标明。模拟器在 Secure Enclave、钥匙串和生物识别方面的行为有所不同。
- 区分编译时错误和运行时错误。SE 密钥导入属于编译时错误。缺少辅助功能类别属于运行时问题(会静默使用错误的默认值)。缺少 OSStatus 检查属于运行时问题(会丢失错误信息)。
Agent 不得做的事:
- 不得捏造 WWDC 会议编号。只能引用参考文件中有记录的会议。
- ✅ 示例必须始终使用原生 API,绝不能使用第三方库代码(KeychainAccess、SAMKeychain、Valet)。当用户明确要求比较原生 API 和第三方库时,应采用建议性语气:客观介绍权衡,不要强硬否定。示例:“原生 API 没有依赖开销;KeychainAccess 和 Valet 可以减少样板代码,但需要依赖第三方的维护周期。” 不要说“此技能不推荐……”,这类指令式输出超出了核心准则。
- 没有证据时,不得声称 Apple API 存在缺陷。应先引导用户排查查询字典错误、缺少的授权和错误的钥匙串,然后再考虑 API 缺陷。
- CryptoKit 已能满足需求时,不得生成 Security 框架代码(iOS 13+)。
- 不得输出不完整的钥匙串操作。绝不展示没有
errSecDuplicateItem回退处理的SecItemAdd。绝不展示没有errSecItemNotFound处理的SecItemCopyMatching。 - 语气不得超出参考文件所支持的程度。
交叉引用协议
- 规范来源: 每种模式都有一个主要参考文件(见上文的参考索引)。
- 简要提及并指向其他文件: 其他文件只需用一句话概述,不要附上完整代码示例。
- Agent 行为: 引用规范文件,并加载该文件以获取详细信息。不要根据次要提及重构模式。
版本基线速查
| API / 功能 | 最低 iOS 版本 | 常见 AI 错误 |
|---|---|---|
| CryptoKit(SHA-2、AES-GCM、P256、ECDH) | 13 | 声称需要 iOS 15+ |
SecureEnclave.P256(CryptoKit) | 13 | 声称需要 iOS 15+ |
SHA-3(SHA3_256、SHA3_384、SHA3_512) | 18 | 声称需要 iOS 26+ |
HPKE(HPKE.Sender、HPKE.Recipient) | 17 | 声称需要 iOS 15+ 或 iOS 18+ |
| ML-KEM / ML-DSA(后量子) | 26 | 与 SHA-3 混淆 |
使用 .biometryCurrentSet 的 SecAccessControl | 11.3 | 声称需要 iOS 13+ |
kSecUseDataProtectionKeychain(macOS) | macOS 10.15 | 在 macOS 上完全遗漏 |
Swift 并发 actor | 13(运行时),17+(建议) | 声称最低要求为 iOS 15 |
LAContext.evaluatedPolicyDomainState | 9 | 不知道其存在 |
NSPinnedDomains(声明式固定) | 14 | 声称需要 iOS 16+ |
Agent 自检清单
定稿任何包含安全代码的回复前,请检查:
- 每个
SecItemAdd都显式设置了kSecAttrAccessible值 - 每个
SecItemAdd都会在遇到errSecDuplicateItem时通过SecItemUpdate回退处理 - 每个
SecItemCopyMatching都会处理errSecItemNotFound - 未将
LAContext.evaluatePolicy()单独用作身份验证门槛 - 未在
@MainActor或主线程上调用SecItem* - macOS 代码包含
kSecUseDataProtectionKeychain: true - Secure Enclave 代码包含
#if targetEnvironment(simulator)守卫 - 未将原始 ECDH 共享密钥用作对称密钥
- 除非用户有记录在案的理由,否则
AES.GCM.seal中不使用显式 nonce - 每条 API 建议都标注了 iOS 版本
- 每种展示的模式都注明了参考文件
- 每项发现都注明了严重程度(审查/审计任务)
- 未编造 WWDC 场次编号