axiom-file-protection-ref by charleswiltgen/axiom
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-file-protection-ref目的:文件加密和数据保护 API 的全面参考 可用性:iOS 4.0+(所有保护级别),iOS 26 中的最新增强功能 背景:基于使用硬件加密的 iOS 数据保护架构构建
在以下情况下使用此技能:
iOS 数据保护提供与设备密码绑定的硬件加速文件加密。当用户设置密码时,每个文件都可以使用受该密码保护的密钥进行加密。
关键概念:
| 级别 | 加密至 | 何时可访问 | 用于 | 后台访问 |
|---|---|---|---|---|
| complete | 设备解锁 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 仅在解锁时 |
| 敏感数据(健康、财务) |
| ❌ 否 |
| completeUnlessOpen | 文件关闭 | 首次解锁后,打开期间 | 大型下载、视频 | ✅ 如果已打开 |
| completeUntilFirstUserAuthentication | 启动后首次解锁 | 首次解锁后 | 大多数应用数据 | ✅ 是 |
| none | 从不 | 始终 | 公共缓存、临时文件 | ✅ 是 |
完整描述:
"文件以加密格式存储在磁盘上,在设备锁定或启动时无法读取或写入。"
用于:
行为:
代码示例:
// ✅ 正确:对敏感数据提供最高安全性
func saveSensitiveData(_ data: Data, to url: URL) throws {
try data.write(to: url, options: .completeFileProtection)
}
// 或在现有文件上设置
try FileManager.default.setAttributes(
[.protectionKey: FileProtectionType.complete],
ofItemAtPath: url.path
)
权衡:
完整描述:
"文件在关闭后以加密格式存储在磁盘上。"
用于:
行为:
代码示例:
// ✅ 正确:在后台下载,但关闭时加密
func startBackgroundDownload(url: URL, destination: URL) throws {
try Data().write(to: destination, options: .completeFileProtectionUnlessOpen)
// 打开文件句柄用于写入
let fileHandle = try FileHandle(forWritingTo: destination)
// 下载在后台继续
// 文件保持可访问,因为它是打开的
// 关闭时,文件变为加密状态
// 稍后,下载完成时:
try fileHandle.close() // 现在加密直到下次解锁
}
权衡:
完整描述:
"文件以加密格式存储在磁盘上,在设备启动后才能访问。"
用于:
行为:
这是大多数文件的推荐默认设置。
代码示例:
// ✅ 正确:为大多数应用数据提供平衡的安全性
func saveAppData(_ data: Data, to url: URL) throws {
try data.write(
to: url,
options: .completeFileProtectionUntilFirstUserAuthentication
)
}
// ✅ 此文件可以在首次解锁后在后台访问
func backgroundTaskCanAccessFile() {
// 即使设备已锁定(首次解锁后),这也有效
let data = try? Data(contentsOf: url)
}
权衡:
完整描述:
"文件没有与之关联的特殊保护。"
用于:
行为:
代码示例:
// ⚠️ 谨慎使用:仅用于真正非敏感的数据
func cachePublicThumbnail(_ data: Data, to url: URL) throws {
try data.write(to: url, options: .noFileProtection)
}
权衡:
// ✅ 推荐:写入时设置保护
let sensitiveData = userData.jsonData()
try sensitiveData.write(
to: fileURL,
options: .completeFileProtection
)
// ✅ 正确:更改现有文件的保护
try FileManager.default.setAttributes(
[.protectionKey: FileProtectionType.complete],
ofItemAtPath: fileURL.path
)
// ✅ 正确:设置目录的默认保护
// 新文件继承此保护
try FileManager.default.setAttributes(
[.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
ofItemAtPath: directoryURL.path
)
// ✅ 检查文件的当前保护级别
func checkFileProtection(at url: URL) throws -> FileProtectionType? {
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
return attributes[.protectionKey] as? FileProtectionType
}
// 用法
if let protection = try? checkFileProtection(at: fileURL) {
switch protection {
case .complete:
print("最高保护")
case .completeUntilFirstUserAuthentication:
print("标准保护")
default:
print("其他保护")
}
}
| 使用场景 | 推荐 | 原因 |
|---|---|---|
| 密码、令牌、密钥 | Keychain | 专为小型机密设计 |
| 小型敏感值(<几KB) | Keychain | 更安全,单独加密 |
| 文件 >1 KB | 文件保护 | Keychain 不适用于大数据 |
| 用户文档 | 文件保护 | 自然的基于文件的存储 |
| 结构化机密 | Keychain | 按键查询,访问控制 |
// ✅ 正确:将小型机密存储在 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)
// ✅ 正确:使用文件保护的文件
let userData = try JSONEncoder().encode(user)
try userData.write(to: fileURL, options: .completeFileProtection)
Keychain 优势:
文件保护优势:
// ❌ 错误:.complete 文件无法在后台访问
class BackgroundTask {
func performBackgroundSync() {
// 如果文件具有 .complete 保护且设备已锁定,此操作将失败
let data = try? Data(contentsOf: sensitiveFileURL)
// 如果设备已锁定,data 将为 nil
}
}
// ✅ 正确:使用 .completeUntilFirstUserAuthentication
// 首次解锁后文件可在后台访问
try data.write(
to: fileURL,
options: .completeFileProtectionUntilFirstUserAuthentication
)
// ✅ 正确:优雅地处理保护错误
func readFile(at url: URL) -> Data? {
do {
return try Data(contentsOf: url)
} catch let error as NSError {
if error.domain == NSCocoaErrorDomain &&
error.code == NSFileReadNoPermissionError {
// 文件受保护且设备已锁定
print("文件受保护,设备已锁定")
return nil
}
throw error
}
}
本地文件保护:
iCloud 加密:
所有 iCloud 数据在静态时加密(Apple 管理的密钥)
某些数据类型提供端到端加密(高级数据保护)
文件保护仅影响本地设备
// ✅ 正确:iCloud 文件的保护仅影响本地副本 func saveToICloud(data: Data, filename: String) throws { guard let iCloudURL = FileManager.default.url( forUbiquityContainerIdentifier: nil ) else { return }
let fileURL = iCloudURL.appendingPathComponent(filename)
// 此保护应用于本地缓存副本
try data.write(to: fileURL, options: .completeFileProtection)
// iCloud 对云存储有单独的加密
}
// ✅ 推荐:在应用启动时设置默认保护
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
)
}
}
// 在应用初始化期间调用
func application(_ application: UIApplication, didFinishLaunchingWithOptions...) {
configureDefaultFileProtection()
return true
}
// ✅ 正确:保护 SwiftData/SQLite 数据库
let appSupportURL = FileManager.default.urls(
for: .applicationSupportDirectory,
in: .userDomainMask
)[0]
let databaseURL = appSupportURL.appendingPathComponent("app.sqlite")
// 在创建数据库之前设置保护
try? FileManager.default.setAttributes(
[.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
ofItemAtPath: appSupportURL.path
)
// 现在创建数据库 - 它继承保护
let container = try ModelContainer(
for: MyModel.self,
configurations: ModelConfiguration(url: databaseURL)
)
// ⚠️ 有时必要:为后台访问降低保护
func enableBackgroundAccess(for url: URL) throws {
try FileManager.default.setAttributes(
[.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
ofItemAtPath: url.path
)
}
// 仅在以下情况下执行此操作:
// 1. 确实需要后台访问
// 2. 数据敏感性允许
// 3. 已考虑安全权衡
症状:后台任务无法读取文件
// 调试:检查当前保护
if let protection = try? FileManager.default.attributesOfItem(
atPath: url.path
)[.protectionKey] as? FileProtectionType {
print("保护:\(protection)")
if protection == .complete {
print("❌ 锁定时无法在后台访问")
}
}
解决方案:改用 .completeUntilFirstUserAuthentication
症状:设备重启后应用无法立即访问文件
原因:使用 .complete 或 .completeUntilFirstUserAuthentication(按设计工作)
解决方案:这是预期行为。要么:
.none(安全权衡)文件保护通常无需特殊权限即可工作,但某些功能需要:
<!-- 需要用于:.complete 保护级别 -->
<key>com.apple.developer.default-data-protection</key>
<string>NSFileProtectionComplete</string>
何时需要:
.complete 保护如何添加:
| 场景 | 推荐保护 | 锁定时可访问? | 后台访问? |
|---|---|---|---|
| 用户健康数据 | .complete | ❌ 否 | ❌ 否 |
| 财务记录 | .complete | ❌ 否 | ❌ 否 |
| 大多数应用数据 | .completeUntilFirstUserAuthentication | ✅ 是(首次解锁后) | ✅ 是 |
| 下载(大文件) | .completeUnlessOpen | ✅ 打开时 | ✅ 打开时 |
| 数据库文件 | .completeUntilFirstUserAuthentication | ✅ 是 | ✅ 是 |
| 下载的图像 | .completeUntilFirstUserAuthentication | ✅ 是 | ✅ 是 |
| 公共缓存 | .none | ✅ 是 | ✅ 是 |
| 临时文件 | .none | ✅ 是 | ✅ 是 |
axiom-storage — 决定何时使用文件保护与其他安全措施axiom-storage-management-ref — 文件生命周期、清除和磁盘管理axiom-storage-diag — 调试文件访问问题最后更新:2025-12-12 技能类型:参考 最低 iOS:4.0(所有保护级别) 最新更新:iOS 26
每周安装
92
仓库
GitHub 星标
590
首次出现
2026年1月21日
安全审计
安装于
opencode77
claude-code72
codex71
gemini-cli70
cursor68
github-copilot66
Purpose : Comprehensive reference for file encryption and data protection APIs Availability : iOS 4.0+ (all protection levels), latest enhancements in iOS 26 Context : Built on iOS Data Protection architecture using hardware encryption
Use this skill when you need to:
iOS Data Protection provides hardware-accelerated file encryption tied to the device passcode. When a user sets a passcode, every file can be encrypted with keys protected by that passcode.
Key concepts :
| Level | Encrypted Until | Accessible When | Use For | Background Access |
|---|---|---|---|---|
| complete | Device unlocked | Only while unlocked | Sensitive data (health, finances) | ❌ No |
| completeUnlessOpen | File closed | After first unlock, while open | Large downloads, videos | ✅ If already open |
| completeUntilFirstUserAuthentication | First unlock after boot | After first unlock | Most app data | ✅ Yes |
| none | Never | Always | Public caches, temp files | ✅ Yes |
Full Description :
"The file is stored in an encrypted format on disk and cannot be read from or written to while the device is locked or booting."
Use For :
Behavior :
Code Example :
// ✅ 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
)
Tradeoffs :
Full Description :
"The file is stored in an encrypted format on disk after it is closed."
Use For :
Behavior :
Code Example :
// ✅ 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
}
Tradeoffs :
Full Description :
"The file is stored in an encrypted format on disk and cannot be accessed until after the device has booted."
Use For :
Behavior :
This is the recommended default for most files.
Code Example :
// ✅ 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)
}
Tradeoffs :
Full Description :
"The file has no special protections associated with it."
Use For :
Behavior :
Code Example :
// ⚠️ USE SPARINGLY: Only for truly non-sensitive data
func cachePublicThumbnail(_ data: Data, to url: URL) throws {
try data.write(to: url, options: .noFileProtection)
}
Tradeoffs :
// ✅ 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")
}
}
| Use Case | Recommended | Why |
|---|---|---|
| Passwords, tokens, keys | Keychain | Designed for small secrets |
| Small sensitive values (<few KB) | Keychain | More secure, encrypted separately |
| Files >1 KB | File Protection | Keychain not designed for large data |
| User documents | File Protection | Natural file-based storage |
| Structured secrets | Keychain | Query by key, access control |
// ✅ 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 advantages :
File protection advantages :
// ❌ 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
}
}
Local file protection :
iCloud encryption :
All iCloud data encrypted at rest (Apple-managed keys)
End-to-end encryption available for some data types (Advanced Data Protection)
File protection only affects local device
// ✅ 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
}
// ✅ 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
}
// ✅ 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)
)
// ⚠️ 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
Symptom : Background tasks fail to read files
// 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")
}
}
Solution : Use .completeUntilFirstUserAuthentication instead
Symptom : App can't access files immediately after device reboot
Cause : Using .complete or .completeUntilFirstUserAuthentication (works as designed)
Solution : This is expected behavior. Either:
.none for files that must be accessible (security tradeoff)File protection generally works without special entitlements, but some features require:
<!-- Required for: .complete protection level -->
<key>com.apple.developer.default-data-protection</key>
<string>NSFileProtectionComplete</string>
When needed :
.complete protectionHow to add :
| Scenario | Recommended Protection | Accessible When Locked? | Background Access? |
|---|---|---|---|
| User health data | .complete | ❌ No | ❌ No |
| Financial records | .complete | ❌ No | ❌ No |
| Most app data | .completeUntilFirstUserAuthentication | ✅ Yes (after first unlock) | ✅ Yes |
| Downloads (large files) | .completeUnlessOpen | ✅ While open | ✅ While open |
axiom-storage — Decide when to use file protection vs other security measuresaxiom-storage-management-ref — File lifecycle, purging, and disk managementaxiom-storage-diag — Debug file access issuesLast Updated : 2025-12-12 Skill Type : Reference Minimum iOS : 4.0 (all protection levels) Latest Updates : iOS 26
Weekly Installs
92
Repository
GitHub Stars
590
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode77
claude-code72
codex71
gemini-cli70
cursor68
github-copilot66
Azure PostgreSQL 无密码身份验证配置指南:Entra ID 迁移与访问管理
34,800 周安装
| Database files | .completeUntilFirstUserAuthentication | ✅ Yes | ✅ Yes |
| Downloaded images | .completeUntilFirstUserAuthentication | ✅ Yes | ✅ Yes |
| Public caches | .none | ✅ Yes | ✅ Yes |
| Temp files | .none | ✅ Yes | ✅ Yes |