跳转至

iOS 应用程序安全清单

阶段 0:治理、环境和 CI/CD

实施 CI 门控 (CI gates)

  • 在每次 PR 上运行静态检查:swiftlint(或同等工具)、单元/UI 测试、依赖项审计。
  • 在出现高/关键 SAST/SCA/DAST 漏洞时阻止发布;在发布分支上保证零 lint 错误。

机密处理

  • 确保源代码、Info.plist、故事板或日志中没有机密。
  • 使用环境/机密管理器来管理机密。
  • 在分析 (analytics) 中隐去机密。
  • 定期以及在怀疑泄漏时轮换密钥/令牌。

证据和可追溯性

  • 将扫描报告与每个制品一起归档。
  • 将 SBOM 与每个制品一起归档。
  • 将构建日志与每个制品一起归档。
  • 将发布说明与每个制品一起归档。

扫描自动化

  • 在 CI/CD 中集成移动 SAST/DAST/SCA 平台,在每个 PR 和预发布中运行(例如,Ostorlab)。
  • 当出现来自 SAST/DAST/SCA 的高/关键问题时,自动使 CI 门控失败。

阶段 1:威胁建模 (Threat modeling)、数据分类和清单

  • 对数据(PII、凭据、令牌、支付)进行分类,并映射收集、处理、存储和传输。
  • 识别信任边界(设备、Keychain/文件、URLSession、WebView、到本地库的平台通道)。
  • 清点第三方 SDK;首选维护良好、已签名、权限最小的 SDK;尽量减少数量。

阶段 2:构建和配置强化 (Xcode + Info.plist)

Xcode 发布设置 (Release settings)

  • 剥离符号和死代码 (dead code)。
  • 启用全模块优化 (whole-module optimization)。
  • 在发布版本中禁用 DEBUG 标志。
  • 从发布版本中排除测试/调试框架。
  • 从发布版本中删除开发端点和功能标志 (feature flags)。

代码签名和授权 (entitlements)

  • 仅授予必要的授权(没有未使用的 App Groups、Keychain Sharing、NFC、Bluetooth 等)。
  • 使用带有专用发布配置文件的自动签名。
  • 保护签名凭据(例如,CI 机密存储、访问控制)。

应用程序传输安全 (App Transport Security - ATS)

  • 设置 NSAllowsArbitraryLoads = false
  • 仅首选 TLS 1.2+。
  • 仅在必要时且在限定时间内使用严格的 NSExceptionDomains

Info.plist 卫生

  • 除非需要,否则禁用文档共享:
  • UIFileSharingEnabled = false
  • LSSupportsOpeningDocumentsInPlace = false
  • 最小化 LSApplicationQueriesSchemes;首选 Universal Links 而不是自定义方案。
  • 为所有权限提供准确的 NS[AppData/AppleEvents/SystemAdministration]UsageDescription 字符串。

阶段 3:本地存储和 Keychain 安全

Keychain 使用

  • 使用尽可能最强的类将令牌/机密存储在 Keychain 中:
  • kSecAttrAccessibleWhenUnlockedThisDeviceOnly
  • 或适用情况下的 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
  • 在 UX 允许的情况下,使用 kSecAccessControl 标志(例如,biometryCurrentSetuserPresence)控制敏感项。
  • 注销时删除 Keychain 项。
  • 考虑对高风险令牌进行设备绑定 (device-binding)。

文件保护和数据库

  • 对文件应用数据保护:NSFileProtectionComplete(默认属性或按文件)。
  • 加密敏感数据库/文件(例如 SQLCipher 或等效项)。
  • 绝不以明文形式存储机密。

缓存和派生数据

  • 避免将 PII 持久化在缓存/临时文件中。
  • 根据需要在注销时清除敏感的缓存/临时数据。
  • 根据需要在后台清除敏感的缓存/临时数据。
  • 不要将机密写入 UserDefaults

阶段 4:隐私保障(UI、剪贴板、屏幕截图)

  • 在退入后台时隐去敏感的 UI(在 sceneWillResignActive / applicationWillResignActive 中模糊或使用占位符)。
  • 检测屏幕捕获(UIScreen.main.isCaptured)并针对高度敏感的视图调整行为。
  • 避免在应用程序启动时读取剪贴板。
  • 绝不将机密放在 UIPasteboard.general 上。

阶段 5:网络安全和 TLS/固定 (pinning)

URLSession/ATS

  • 强制对受信任主机使用 HTTPS。
  • 拒绝无效的证书和主机名。
  • 禁用 HTTP 回退 (fallbacks)。
  • 设置严格的超时。
  • 在适当的情况下实施带有抖动 (jitter) 的重试。
  • 在 TLS 错误时采用故障关闭 (fail closed)。
  • 强制在服务器 TLS 设置上使用 TLS 1.2+。

证书固定 (Certificate pinning)

  • 优先使用 SPKI SHA-256 固定。
  • 为每个主机包含至少一个备用固定。
  • 通过 URLSessionDelegate 信任评估实施固定。
  • 验证针对 MITM 尝试的行为。
  • 验证过期/轮换的叶子证书的行为。
  • 计划安全的固定轮换。
  • 包含有关固定失败的遥测数据。

Cookies 和令牌

  • 对 Web 流程使用安全、HttpOnly、SameSite 的 cookies。
  • 避免在设备上使用长期不记名令牌 (bearer tokens)。
  • 在敏感会话后清除 cookies 和 Web 数据。

阶段 6:身份验证、授权和会话

身份验证流程

  • 为公共客户端使用带有 PKCE 的 OAuth2/OIDC。
  • 绝不能在应用程序中嵌入客户端机密。
  • 首选系统浏览器(ASWebAuthenticationSession / SafariViewController)而不是 WebView 进行身份验证。

令牌

  • 使用短期访问令牌 (access tokens)。
  • 将刷新令牌 (refresh tokens) 存储在 Keychain 中。
  • 在注销时轮换并使令牌失效。
  • 在更换设备时轮换并使令牌失效。
  • 在适当的情况下将会话绑定到设备风险 (device risk)。
  • 检测异常(地理位置/IP/ASN 更改)并做出响应。

授权

  • 在服务器上强制执行授权;将客户端检查视为建议性参考。
  • 验证是否存在 IDOR/BOLA 漏洞。
  • 验证是否存在特权升级问题。

阶段 7:WebView 强化 (WKWebView)

  • 仅在需要时使用 WKWebView
  • 首选应用程序绑定的域以实现隔离(WKAppBoundDomains)。

默认值

  • 除非必要,否则禁用 JavaScript。
  • 禁用 javaScriptCanOpenWindowsAutomatically
  • 通过 WKNavigationDelegate 设置导航允许列表。
  • 拦截 file:// URLs。
  • 拦截 javascript: URLs。
  • 为敏感会话使用非持久性数据存储。
  • 在注销时清除 HTTPCookieStore 和网站数据。
  • 确保在发布版本中禁用 Web Inspector(iOS 16+ isInspectable = false)。

内容安全

  • 对于第一方 Web 内容,强制执行 CSP。
  • 强制执行 HTTPS。
  • 不允许混合内容。

  • 首选 Universal Links。
  • 验证传入链接的来源和参数。

如果使用自定义 URL 方案

  • 选择一个独特的方案。
  • 验证所有参数。
  • 防御 URL 劫持。
  • 防御开放重定向。
  • LSApplicationQueriesSchemes 限制为已知且必要的方案。

阶段 9:逆向工程和防篡改能力 (tampering)

减少静态泄露

  • 从发布到商店的二进制文件中剥离符号。
  • 在服务器端存储 dSYM 以进行崩溃符号化。
  • 删除发布版本中的详细日志。
  • 删除发布版本中的调试菜单。
  • 删除发布版本中的测试端点。
  • 删除不打算用于生产环境的功能切换 (feature toggles)。

环境检查

  • 记录有关越狱存在(文件、系统调用、可疑库)的信号。
  • 记录有关钩子框架的信号。
  • 记录有关调试器存在的信号。
  • 按比例响应(降级/阻止/遥测),而不完全依赖于客户端检查。

篡改测试 (Tamper testing)

  • 验证重新打包的构建是否在完整性检查或服务器证明中失败。
  • 验证重新签名的构建是否在完整性检查或服务器证明中失败。
  • 将客户端检查视为信号,绝不能作为唯一的安全控制手段。

阶段 10:密码学和随机性

  • 使用平台加密(CryptoKit / CommonCrypto);不要实现自定义加密。
  • 对认证加密使用 AES-GCM 或 ChaCha20-Poly1305。
  • 使用 HKDF/PBKDF2 作为 KDF,配合适当的盐和充分的参数。
  • 使用 SecRandomCopyBytes 生成密钥/IVs。
  • 绝不重复使用 IV/随机数 (nonces)。
  • 将加密密钥存储在 Keychain 中。

阶段 11:权限、通知和标识符

  • 仅请求所需的最低权限。
  • 在使用的地点请求权限。
  • 请求权限时,向用户清楚地解释价值主张。

ATT (应用跟踪透明度)

  • 仅在必要时请求 ATT。
  • 如果 ATT 被拒绝,则优雅地 (gracefully) 降级。
  • 避免指纹识别 (fingerprinting) 和类似的禁止追踪技术。

标识符

  • 首选 identifierForVendor
  • 除非已授予 ATT,否则避免使用 IDFA。
  • 绝不使用禁止的持久性标识符。

阶段 12:日志记录、分析 (analytics) 和崩溃报告

  • 使用带有隐私注释的 os_log
  • 禁用发布版本中的详细日志。
  • 从日志中隐去 PII。
  • 从日志中隐去机密。
  • 从分析事件中隐去 PII 和机密。
  • 从崩溃报告中隐去 PII 和机密。
  • 尽可能对分析数据进行采样和聚合。
  • 确保在服务器端发生崩溃符号化。
  • 绝不将符号与应用程序一起发送。

阶段 13:测试、DAST 和运行时验证

流量拦截测试

  • 验证对具有无效证书的 MITM 的抵抗力。
  • 验证对主机名不匹配的 MITM 的抵抗力。
  • 验证对不受信任的 CA 的 MITM 的抵抗力。
  • 确认在实施了固定的地方,固定能够阻止拦截。

运行时检查

  • 确认日志中没有机密/PII。
  • 确认 Keychain 项以正确的 ACL(访问控制列表)存在。
  • 确认文件具有 NSFileProtectionComplete(或在需要时更严格)。

API 滥用测试

  • 测试速率限制 (rate limiting)。
  • 测试重放保护 (replay protections)。
  • 测试分页边界。
  • 测试批量赋值 (mass assignment) 漏洞。
  • 测试详细的错误泄露。

动态分析

  • 运行移动 DAST,同时执行核心流程以发现传输错误配置。
  • 使用 DAST 发现运行时问题(例如,使用 Ostorlab)。
  • 导出证据用于分类和重新测试。

阶段 14:SBOM 和证据

SBOM 和依赖项

  • 为 Swift/Objective-C 代码生成 SBOM。
  • 为嵌入的 SDK 生成 SBOM。
  • 跟踪影响依赖项的 CVE。
  • 根据风险定期更新依赖项。

证据包

  • 将扫描工件附加到发布版本中。
  • 将 SBOM 附加到发布版本中。
  • 将 Info.plist 差异附加到发布版本中。
  • 将授权 (entitlements) 列表附加到发布版本中。
  • 将测试报告附加到发布版本中。

阶段 15:报告和标准对齐

报告

  • 制作执行摘要。
  • 记录范围和方法。
  • 记录包含 PoC 和证据的详细发现。
  • 为发现分配风险评级。
  • 提供修复指导。
  • 包含重新测试结果。

OWASP MASVS 映射

  • PLATFORM:阶段 2、7–8、11。
  • STORAGE:阶段 3–4。
  • CRYPTO:阶段 10。
  • AUTH:阶段 6。
  • NETWORK:阶段 5。
  • CODE:阶段 2、9、12。
  • RESILIENCE:阶段 9。

从业人员快速参考

  • Info.plist/ATS:没有任意加载;严格的异常域;除非需要,否则禁用文档/文件共享。
  • Keychain:ThisDeviceOnly 类;可选的 biometryCurrentSet/userPresence;注销时删除机密。
  • 文件:NSFileProtectionComplete;加密数据库/文件;避免将 UserDefaults 用于机密。
  • Network:仅 HTTPS;URLSessionDelegate 固定(带有备份的 SPKI SHA-256);验证 MITM 尝试失败。
  • Auth:OIDC + PKCE;短期令牌;Keychain 中的刷新令牌;服务器端授权;测试 IDOR/BOLA。
  • WebView:首选系统浏览器进行身份验证;如果是 WKWebView,默认关闭 JS,导航允许列表,应用程序绑定的域,敏感流程的非持久存储。
  • 隐私:屏蔽后台快照;避免剪贴板机密;最小化权限并保持准确的使用字符串。
  • 发布卫生:剥离应用程序中的符号,保持 dSYM 私密;删除调试日志/菜单;验证授权 (entitlements)。

示例配置和代码片段

ATS (Info.plist)

  • 配置 NSAppTransportSecurity
  • NSAllowsArbitraryLoads = false
  • NSExceptionDomains = { yourdomain.com: { NSTemporaryExceptionAllowsInsecureHTTPLoads = false, NSIncludesSubdomains = true, NSTemporaryExceptionMinimumTLSVersion = TLSv1.2 } }

文件保护 (Swift)

  • 使用完整的文件保护:
  • try data.write(to: url, options: .completeFileProtection)

Keychain 项(Swift 概要)

  • 配置 Keychain 项:
  • kSecClass: kSecClassGenericPassword
  • kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
  • kSecAttrAccessControl: SecAccessControlCreateWithFlags(..., [.biometryCurrentSet, .userPresence])

URLSession 固定(大纲)

  • urlSession(_:didReceive:completionHandler:) 中:
  • 评估 SecTrust
  • 从证书中提取 SPKI。
  • 将 SPKI 的 SHA-256 与固定允许列表进行比较。
  • 适当地选择 .useCredential.cancel

轻量级工作流程

合并前门控 (Pre-merge gate)

  • 运行 lint 和测试。
  • 运行依赖项审计。
  • 触发 SAST/SCA 扫描。
  • 在出现高/关键问题时阻止合并。

预发布阶段 (Pre-release staging)

  • 验证 Info.plist 和授权 (entitlements)。
  • 构建发布二进制文件。
  • 确认符号没有随应用程序一起发送。
  • 在执行核心用户流程时运行 DAST(例如,使用 Ostorlab)以验证 TLS/固定、存储保护和日志记录卫生。
  • 修复问题并重新测试。

发布后监控 (Post-release monitoring)

  • 跟踪崩溃和性能。
  • 按照定义的计划轮换密钥/令牌。
  • 监控 SDK 建议和 CVE。
  • 保持 SCA 监视第三方 SDK/库(例如 Ostorlab)以针对新漏洞发出警报。
  • 在发现新漏洞时触发有针对性的重新测试。