Integrate privacy into your development process
Privacy & Security 进阶 0m

将隐私保护融入开发全流程

Integrate privacy into your development process

2025年6月9日

在 Apple 官方观看视频

一句话判断

这场 Session 不是讲某个新 API,而是一套从规划到部署的隐私工程方法论——对有上架需求的团队来说,它是 App Store 审核合规的实操指南。

这场 Session 讲了什么

主讲人 Joey Tyson 是 Apple 的隐私工程师,他把 app 开发的完整生命周期拆成五个阶段——规划、设计、开发、测试、部署,逐一讲解每个阶段如何融入隐私保护。这不是抽象的隐私原则课,而是非常具体的操作手册。

规划阶段的核心产出是”隐私保证声明”(privacy assurance),用高层级的语言定义用户可以期待什么。Apple 用四大支柱来引导这些声明:数据最小化、设备端处理、透明与控制、安全保护。对于一个虚拟的地标社交 app “Pal About”,主讲人逐步定义了”不默认存储位置""只在本地生成建议""照片训练需要 opt-in”等具体保证。

开发阶段覆盖的技术点很多:PhotosPicker 避免整个相册权限弹窗、Location Button 实现一次性位置共享、CloudKit 的端到端加密字段、同态加密和 PIR(Private Information Retrieval)实现服务器看不到查询内容的数据检索、Private Access Tokens 替代 CAPTCHA、DeviceCheck 做匿名设备状态标记、AdAttributionKit 在不需要 ATT 弹窗的情况下做广告归因。

测试阶段强调把隐私保证写成可测试的用例,包括 unit test 验证隐私控制逻辑、integration test 验证数据流、UI test 验证 opt-in 设置对数据流的影响。部署阶段则详细解释了隐私营养标签(Privacy Nutrition Label)的填写规则和注意事项。

值得深挖的点

PIR(Private Information Retrieval)的实际应用与 trade-off

同态加密和 PIR 是这场 Session 里最硬核的技术点。PIR 的核心能力是:客户端向服务器查询数据时,服务器在加密状态下完成计算,全程不知道查询了什么、返回了什么。客户端发送加密的查询 payload,服务器用同态加密计算加密的结果,返回后客户端本地解密。

对于”Pal About”这个 app,主讲人用 PIR 来查询附近聚会地点的实时信息——服务器无法追踪用户的搜索历史。他特别提到,某些用户探索的位置信息可能影响其安全性,所以工程上选择 PIR 不只是隐私优化,更是安全需求。

trade-off 在于性能和实现复杂度。同态加密的计算开销远大于明文操作,PIR 适合查询频率中等、隐私敏感度高的场景(如位置查询、健康数据查询),不适合高频低延迟的场景(如搜索建议)。Apple 在 GitHub 上开源了 Swift Homomorphic Encryption 库,降低了接入门槛,但你仍然需要评估自己的服务端架构是否能承受加密计算的额外开销。

另一个容易被忽略的点是:PIR 保护的是”查询行为”不被服务器观察到,不保护”查询结果”本身。如果返回的数据包含敏感信息,你仍然需要在客户端做额外的保护处理。

数据最小化在 AI 时代的实践

Session 反复强调”data minimization”,但在 AI/ML 功能越来越多的当下,这个原则面临实际挑战。训练模型需要数据,个性化需要数据,而用户的期望是”尽量少收集”。

Apple 的立场很明确:默认不收集,只有在用户 opt-in 且获得明确价值回报时才收集。具体到 Pal About 的例子:照片上传默认不用于训练生成式模型,只有用户主动选择”改进智能功能”时才使用。

这个策略的 trade-off 是:opt-in 意味着数据量大幅减少,模型迭代速度变慢。但从用户信任的角度看,这比”默认收集、提供 opt-out”要好得多。Apple 的做法是把选择权交给用户,同时确保 opt-in 的价值回报足够明确——不是空泛的”帮助改进产品”,而是具体的”生成更好的推荐”。

对开发者的实际建议是:在规划阶段就把数据收集的范围写进隐私保证声明,而不是在开发阶段根据功能需要临时扩大数据收集范围。

代码片段

1. PhotosPicker 避免相册权限弹窗

场景:让用户选择照片上传,不需要请求整个相册的访问权限。

import PhotosUI

struct PhotoUploadView: View {
    @State private var pickerItem: PhotosPickerItem?

    var body: some View {
        PhotosPicker(
            selection: $pickerItem,
            matching: .images
        ) {
            Label("Choose Photo", systemImage: "photo")
        }
    }
}

坑:PhotosPicker 是 out-of-process 运行的,app 只拿到用户实际选中的照片,永远不会接触整个相册。但如果你后续需要访问原图的 metadata(如拍摄位置),仍然可能触发额外权限请求。

2. CloudKit 加密字段

场景:在 CloudKit 中存储用户敏感数据,支持 Advanced Data Protection 端到端加密。

let record = CKRecord(recordType: "Message")
record.encryptedValues["body"] = "Secret content"
record.encryptedValues["senderName"] = "Alice"

// 读取加密字段
let body = record.encryptedValues["body"] as? String

坑:CloudKit 不支持对加密字段建索引,所以不能在 CKQuery 的 predicate 或 sort descriptor 中使用加密字段。如果需要按加密字段查询,必须在客户端拉取全量数据后本地过滤。

3. Location Button 一次性位置共享

场景:获取用户当前位置,首次确认后后续不再弹权限提示。

import CoreLocationUI

LocationButton(.shareCurrentLocation) {
    locationManager.requestLocation()
}
.labelStyle(.iconOnly)
.cornerRadius(8)

坑:Location Button 首次点击会弹出确认弹窗,之后不再提示。系统会在状态栏显示位置指示器。但如果 app 没有 active 的位置使用场景,不要使用这个按钮——它会让用户觉得你在后台偷偷获取位置。

最佳实践

  • 在规划阶段用四大支柱(数据最小化、设备端处理、透明与控制、安全保护)定义隐私保证声明,再从声明推导工程需求。
  • 优先使用系统提供的 picker(PhotosPicker、ContactPicker)替代广权限 API,避免不必要的权限弹窗。
  • 权限弹窗不要在 app 首次启动时触发,放在用户真正需要该功能时再请求,并在 purpose string 中清楚说明原因。
  • 在 App Privacy Report 中定期检查 app 的数据访问和网络活动,确保与用户预期一致。
  • 将隐私测试纳入 CI:写 UI test 验证 opt-in 设置确实改变了数据流,写 integration test 验证 CloudKit 加密字段的读写行为。
  • 上架前用 Xcode archive 的 “Generate Privacy Report” 功能汇总所有 privacy manifest,确保营养标签覆盖所有数据使用场景。

还有什么值得关注

  • macOS Tahoe 会启发式检测 app 退出后是否仍有子进程在运行(通过 fork/exec/POSIX spawn 启动的),如果发现会提示用户确认。
  • AdAttributionKit 的 API 调用不需要展示 ATT 弹窗,对做广告投放的 app 来说是隐私和体验的双赢。
  • 所有第三方 SDK 都可能需要 privacy manifest 文件,描述它们收集的数据和使用的 required reason API,提交前务必检查依赖库的合规性。
隐私与安全