mcpField
A
axiom-file-protection-ref

axiom-file-protection-ref

github.com/charleswiltgen安全1.2k177 次安装今天更新
代码仓库

关于 axiom-file-protection-ref

面向 iOS 开发者的文件保护 API 参考技能,介绍不同保护级别、文件可访问时机、后台行为,以及与 Keychain 的取舍。

主要功能

  1. 01比较四种文件保护级别
  2. 02设置新文件和现有文件的保护
  3. 03检查文件当前保护级别
  4. 04评估后台访问行为
  5. 05比较文件保护与 Keychain
  6. 06排查受保护文件的访问问题

适用场景

  1. 01为敏感文档选择保护级别
  2. 02解决设备锁定时的文件访问失败
  3. 03支持后台任务读取文件
  4. 04为机密信息与大文件选择存储方式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-file-protection-ref

安装后在 Claude Code 中输入/axiom-file-protection-ref确认已加载。

详细介绍

iOS 文件保护参考

用途:文件加密和数据保护 API 的综合参考 可用性:iOS 4.0+(所有保护级别),iOS 26 中的最新增强功能 背景:基于 iOS 数据保护架构,使用硬件加密

何时使用此技能

在以下情况下使用此技能:

  • 保护静态存储的敏感用户数据
  • 为文件选择合适的 FileProtectionType
  • 了解文件何时可访问/加密
  • 调试设备锁定后出现的“无法访问文件”错误
  • 实现安全文件存储
  • 比较 Keychain 与文件保护方案
  • 处理后台文件访问需求

概述

iOS 数据保护提供与设备密码关联的硬件加速文件加密。用户设置密码后,每个文件都可以使用受该密码保护的密钥进行加密。

核心概念:

  • 启用保护后,文件会自动加密
  • 加密密钥由设备硬件和用户密码派生
  • 设备锁定时,文件会变得无法访问(具体取决于保护级别)
  • 不会产生性能开销(硬件 AES 加密)

保护级别对比

级别加密持续到可访问时机用途后台访问
complete设备解锁仅在解锁期间敏感数据(健康、财务)❌ 否
completeUnlessOpen文件关闭首次解锁后,文件打开期间大型下载、视频✅ 文件已打开时可访问
completeUntilFirstUserAuthentication开机后首次解锁首次解锁后大多数应用数据✅ 是
none从不始终公开缓存、临时文件✅ 是
保护级别详细说明
.complete

完整说明:

“文件以加密格式存储在磁盘上;设备锁定或启动时,无法读取或写入该文件。”

适用场景:

  • 用户健康数据
  • 财务信息
  • 密码库
  • 敏感文档
  • 个人照片(如果应用需要最高安全性)

行为:

  • 已加密:✅ 始终
  • 可访问:仅在设备解锁时
  • 后台访问:❌ 否(设备锁定时应用无法读取)
  • 开机后可用:❌ 否(直到用户解锁)

代码示例:

// ✅ CORRECT: Maximum security for sensitive data
func saveSensitiveData(_ data: Data, to url: URL) throws {
    try data.write(to: url, options: .completeFileProtection)
}

// Or set on existing file
try FileManager.default.setAttributes(
    [.protectionKey: FileProtectionType.complete],
    ofItemAtPath: url.path
)

权衡:

  • ✅ 最高安全性
  • ❌ 无法在后台访问
  • ❌ 应用尝试在设备锁定时访问时,用户会看到错误
.completeUnlessOpen

完整说明:

“文件关闭后,会以加密格式存储在磁盘上。”

适用场景:

  • 大型文件下载(可在后台继续)
  • 正在播放的视频文件
  • 正在编辑的文档
  • 任何需要在打开期间支持后台访问的文件

行为:

  • 已加密:✅ 关闭时
  • 可访问:首次解锁后可访问,文件打开期间仍可访问
  • 后台访问:✅ 是(文件已打开时)
  • 开机后可用:❌ 否(直到首次解锁)

代码示例:

// ✅ CORRECT: Download in background, but encrypted when closed
func startBackgroundDownload(url: URL, destination: URL) throws {
    try Data().write(to: destination, options: .completeFileProtectionUnlessOpen)

    // Open file handle for writing
    let fileHandle = try FileHandle(forWritingTo: destination)

    // Download continues in background
    // File remains accessible because it's open
    // When closed, file becomes encrypted

    // Later, when download complete:
    try fileHandle.close()  // Now encrypted until next unlock
}

权衡:

  • ✅ 安全性良好(不使用时会加密)
  • ✅ 支持后台访问(文件已打开时)
  • ⚠️ 文件打开期间存在风险
.completeUntilFirstUserAuthentication

完整说明:

“文件以加密格式存储在磁盘上;设备启动后才能访问。”

适用场景:

  • 大多数应用数据
  • 用户偏好设置
  • 已下载内容
  • 数据库文件
  • 任何需要后台访问的内容

行为:

  • 已加密:✅ 始终
  • 可访问:开机后首次解锁后
  • 后台访问:✅ 是(首次解锁后)
  • 开机后可用:❌ 否(直到用户至少解锁一次)

对于大多数文件,这是推荐的默认设置。

代码示例:

// ✅ CORRECT: Balanced security for most app data
func saveAppData(_ data: Data, to url: URL) throws {
    try data.write(
        to: url,
        options: .completeFileProtectionUntilFirstUserAuthentication
    )
}

// ✅ This file can be accessed in background after first unlock
func backgroundTaskCanAccessFile() {
    // This works even if device is locked (after first unlock)
    let data = try? Data(contentsOf: url)
}

权衡:

  • ✅ 启动期间受保护(设备关机时被盗)
  • ✅ 支持后台访问(正常运行期间)
  • ⚠️ 设备锁定时仍可访问(保护性低于 .complete)
.none

完整说明:

“此文件没有任何特殊保护。”

适用场景:

  • 公共缓存数据
  • 临时文件
  • 非敏感下载内容
  • 仅在绝对必要时使用

行为:

  • 加密:❌ 从不
  • 可访问:✅ 始终
  • 后台访问:✅ 始终
  • 启动后可用:✅ 始终

代码示例:

// ⚠️ USE SPARINGLY: Only for truly non-sensitive data
func cachePublicThumbnail(_ data: Data, to url: URL) throws {
    try data.write(to: url, options: .noFileProtection)
}

权衡:

  • ✅ 始终可访问
  • ❌ 无加密
  • ❌ 设备被盗时容易受到攻击

设置文件保护

创建文件时
// ✅ RECOMMENDED: Set protection when writing
let sensitiveData = userData.jsonData()
try sensitiveData.write(
    to: fileURL,
    options: .completeFileProtection
)
对现有文件
// ✅ CORRECT: Change protection on existing file
try FileManager.default.setAttributes(
    [.protectionKey: FileProtectionType.complete],
    ofItemAtPath: fileURL.path
)
目录的默认保护
// ✅ CORRECT: Set default protection for directory
// New files inherit this protection
try FileManager.default.setAttributes(
    [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
    ofItemAtPath: directoryURL.path
)
检查当前保护
// ✅ Check file's current protection level
func checkFileProtection(at url: URL) throws -> FileProtectionType? {
    let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
    return attributes[.protectionKey] as? FileProtectionType
}

// Usage
if let protection = try? checkFileProtection(at: fileURL) {
    switch protection {
    case .complete:
        print("Maximum protection")
    case .completeUntilFirstUserAuthentication:
        print("Standard protection")
    default:
        print("Other protection")
    }
}

文件保护与 Keychain

决策矩阵
用例推荐方案原因
密码、令牌、密钥Keychain专为小型机密信息设计
小型敏感值(<几 KB)Keychain更安全,单独加密
文件 >1 KB文件保护Keychain 并非为大型数据设计
用户文档文件保护适合基于文件的存储
结构化机密信息Keychain可按键查询并进行访问控制
代码对比
// ✅ CORRECT: Small secrets in Keychain
let passwordData = password.data(using: .utf8)!
let query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrAccount as String: "userPassword",
    kSecValueData as String: passwordData,
    kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
]
SecItemAdd(query as CFDictionary, nil)

// ✅ CORRECT: Files with file protection
let userData = try JSONEncoder().encode(user)
try userData.write(to: fileURL, options: .completeFileProtection)

Keychain 的优势:

  • 更精细的访问控制(Face ID/Touch ID)
  • 单独加密(不依赖文件系统)
  • 应用删除后仍然保留(如果已配置)

文件保护的优势:

  • 可与现有文件操作配合使用
  • 高效处理大型数据
  • 只需少量代码即可自动生效

后台访问注意事项

iOS 后台模式与文件保护
// ❌ WRONG: .complete files can't be accessed in background
class BackgroundTask {
    func performBackgroundSync() {
        // This FAILS if file has .complete protection and device is locked
        let data = try? Data(contentsOf: sensitiveFileURL)
        // data will be nil if device locked
    }
}

// ✅ CORRECT: Use .completeUntilFirstUserAuthentication
// Files accessible in background after first unlock
try data.write(
    to: fileURL,
    options: .completeFileProtectionUntilFirstUserAuthentication
)
处理保护错误
// ✅ CORRECT: Handle protection errors gracefully
func readFile(at url: URL) -> Data? {
    do {
        return try Data(contentsOf: url)
    } catch let error as NSError {
        if error.domain == NSCocoaErrorDomain &&
           error.code == NSFileReadNoPermissionError {
            // File is protected and device is locked
            print("File protected, device locked")
            return nil
        }
        throw error
    }
}

iCloud 与文件保护

文件保护如何与 iCloud 配合使用

本地文件保护:

  • 应用于本地缓存副本
  • 不会影响存储在 iCloud 中的版本
  • iCloud 有自己的加密机制(传输中和静态数据)

iCloud 加密:

  • 所有 iCloud 数据在静态存储时都会加密(由 Apple 管理密钥)
  • 某些数据类型支持端到端加密(高级数据保护)
  • 文件保护只影响本地设备
// ✅ CORRECT: Protection on iCloud file affects local copy only
func saveToICloud(data: Data, filename: String) throws {
    guard let iCloudURL = FileManager.default.url(
        forUbiquityContainerIdentifier: nil
    ) else { return }

    let fileURL = iCloudURL.appendingPathComponent(filename)

    // This protection applies to local cached copy
    try data.write(to: fileURL, options: .completeFileProtection)

    // iCloud has separate encryption for cloud storage
}

常见模式

模式 1:新应用的默认保护
// ✅ RECOMMENDED: Set default protection at app launch
func configureDefaultFileProtection() {
    let fileManager = FileManager.default

    let directories: [FileManager.SearchPathDirectory] = [
        .documentDirectory,
        .applicationSupportDirectory
    ]

    for directory in directories {
        guard let url = fileManager.urls(
            for: directory,
            in: .userDomainMask
        ).first else { continue }

        try? fileManager.setAttributes(
            [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
            ofItemAtPath: url.path
        )
    }
}

// Call during app initialization
func application(_ application: UIApplication, didFinishLaunchingWithOptions...) {
    configureDefaultFileProtection()
    return true
}
模式 2:加密数据库文件
// ✅ CORRECT: Protect SwiftData/SQLite database
let appSupportURL = FileManager.default.urls(
    for: .applicationSupportDirectory,
    in: .userDomainMask
)[0]

let databaseURL = appSupportURL.appendingPathComponent("app.sqlite")

// Set protection before creating database
try? FileManager.default.setAttributes(
    [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
    ofItemAtPath: appSupportURL.path
)

// Now create database - it inherits protection
let container = try ModelContainer(
    for: MyModel.self,
    configurations: ModelConfiguration(url: databaseURL)
)
模式 3:为后台任务降低保护级别
// ⚠️ SOMETIMES NECESSARY: Lower protection for background access
func enableBackgroundAccess(for url: URL) throws {
    try FileManager.default.setAttributes(
        [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
        ofItemAtPath: url.path
    )
}

// Only do this if:
// 1. Background access is truly required
// 2. Data sensitivity allows it
// 3. You've considered security tradeoffs

调试文件保护问题

问题:后台无法访问文件

症状:后台任务读取文件失败

// Debug: Check current protection
if let protection = try? FileManager.default.attributesOfItem(
    atPath: url.path
)[.protectionKey] as? FileProtectionType {
    print("Protection: \(protection)")
    if protection == .complete {
        print("❌ Can't access in background when locked")
    }
}

解决方案:改用 .completeUntilFirstUserAuthentication

问题:重启后无法访问文件

症状:设备重启后,应用无法立即访问文件

原因:使用了 .complete 或 .completeUntilFirstUserAuthentication(符合预期)

解决方案:这是预期行为。可以:

  1. 等待用户解锁设备
  2. 通过适当的 UI 优雅处理
  3. 对必须可访问的文件使用 .none(安全性权衡)

权限配置

文件保护通常无需特殊权限配置,但某些功能需要:

数据保护权限

<key>com.apple.developer.default-data-protection</key>
<string>NSFileProtectionComplete</string>

需要时机:

  • 使用 .complete 保护
  • 某些 iOS 版本要求使用任何保护级别时都配置(请查阅文档)

添加方法:

  1. Xcode → Target → Signing & Capabilities
  2. “+ Capability” → Data Protection
  3. 选择保护级别

快速参考表

场景推荐的保护级别锁定时是否可访问?是否可在后台访问?
用户健康数据.complete❌ 否❌ 否
财务记录.complete❌ 否❌ 否
大多数应用数据.completeUntilFirstUserAuthentication✅ 是(首次解锁后)✅ 是
下载内容(大文件).completeUnlessOpen✅ 打开时可访问✅ 打开时可访问
数据库文件.completeUntilFirstUserAuthentication✅ 是✅ 是
已下载的图片.completeUntilFirstUserAuthentication✅ 是✅ 是
公开缓存.none✅ 是✅ 是
临时文件.none✅ 是✅ 是

相关技能

  • axiom-storage — 确定何时使用文件保护,而非其他安全措施
  • axiom-storage-management-ref — 文件生命周期、清理和磁盘管理
  • axiom-storage-diag — 调试文件访问问题
  • axiom-keychain — 安全存储凭据(令牌、密码、密钥)
  • axiom-keychain-ref — 完整的 SecItem API 参考
  • axiom-cryptokit — 使用 CryptoKit 加密和签名

最后更新:2025-12-12 技能类型:参考 最低 iOS 版本:4.0(所有保护级别) 最新更新:iOS 26

评论

支持 Markdown