这个话题的起因来自 2023 年 WWDC 之后苹果发布的「App Store 提交隐私更新」政策,政策主要提出了两点:第三方 SDK 隐私清单和签名 和需要提供必要理由的 API 流程。
其实先简单总结,就是 Apple 想通过隐私清单来进一步提升用户数据收集和使用的透明度 ,包括 required reason API 也是算是属于隐私清单里的内容。
那这个和标题里的 Flutter 有什么关系?因为跟随此次隐私清单升级, Apple 上线了一个**对用户隐私产生重大影响的第三方 SDK 列表** ,而在这列表恰好就有 Flutter 的存在,并且里面接近 1/3 是和 Flutter 相关的 Plugin ,例如 connectivity_pulus
、device_info_plus
、fluttertoast
、shared_preferences_ios
、sqflite
、webview_flutter_wkwebview
、url_launcher
等等。
这就很懵逼了,是 Flutter 做了什么「天怒人怨」的问题,还是 Apple 故意针对 Flutter 呢?那在聊这个问题之前,我们首先需要简单了解下,这次的隐私标签里主要涉及的是什么?
这个问题不只是对 Flutter ,其实对于 iOS 来说 2024 在 Xcode15 上也属于最重要的适配需求。
隐私清单
**聊隐私清单,首先最主要是就是第三方隐私清单(privacy manifest) **,属于让 SDK 开发人员提供他们 SDK 的一些数据收集行为,然后 App 开发者在集成各种 SDK 之后,可以得到一份详细的隐私清单,如下图是 SDK 里声明的隐私清单。
隐私清单的作用是帮助开发者了解第三方 SDK 如何使用数据,这样开发者在向 App Store 提交审核时,Xcode 可以将第三方 SDK 中的隐私清单合并,然后导出一个 PDF 报告汇总。
通过这份隐私清单报告,开发者就可以在 App Store 提交审核时,更方便地提供「隐私标签」,如下图所示就是 iOS 14 时 App Store 推出的功能,当时就要求 App 开发者在 App Store 后台声明应用的数据收集和使用场景,让用户在应用详情页面知道 App 收集了什么和做了什么。
那么首先可以知道,SDK 隐私清单的作用,就是让 App 开发者可以根据生成的清单,更好精确和方便地管理自己 App 的隐私标签,一般情况下大概会有:
-
收集的数据类型
-
数据是否能关联到用户
-
数据是否用于跟踪活动
-
收集此数据的理由
其中关于数据类型可见:developer.apple.com/documentati...
那如果我不需要隐私清单来管理隐私标签,就可以理会这次的隐私清单的事情了呢?那答案肯定不是,因为隐私标签还包含了其他东西,比如必要理由的 API 声明、声明跟踪域名和第三方 SDK 签名等。
必要理由的 API 声明
首先这个主要是 Apple 新搞了一个 API 分类, 使用这一个 API 分类你需要在隐私清单里说明使用理由, Apple 这么做的目的是主要是想规范 App 随意通过这些 API 做 fingerprinting 识别的行为,相关的 API 也不多,大概涉及:
- File timestamp APIs
- System boot time APIs
- Disk space APIs
- Active keyboard APIs
- User defaults APIs
如果 SDK 和 App 里用到了分类里的这些 API ,你就需要在隐私列表里填写原因,例如:
使用了 Disk space APIs 的
systemFreeSize
,在写入前判断磁盘空间大小,那么就需要引用列表里的E174.1
条款,如下图写明使用的原因。
从目前看,类似 UserDefaults
、stat
等 API 还是很容易被使用到,所以 Required Reason API 这部分应该都是跑不掉的。
声明跟踪域名
隐私清单其实一定程度也算是应用跟踪透明度 (ATT) 的内容之一,而本次更新,如果用户未通过 App Tracking Transparency (ATT) 授予权限, Apple 就会阻止对追踪域(Tracking Domains)的网络请求。
简单来说,就是需要定义一个 NSPrivacyTrackingDomains
列表,这样但用户不同意 ATT 权限时, 系统就会阻止这些域名的任何调用,所以 SDK 上如果有类似相关跟踪的域名调用,也需要区分声明好跟踪域名和非跟踪域名。
当然,不管你是否利用 ATT 的定义进行跟踪,你都必须在隐私清单中列出收集的数据类型和原因,因为这个和 ATT 没有必然关系,只是如果使用了 ATT 的域名,在这里需要额外声明来符合权限, ATT 在 iOS 17 上实际并没有什么变化。
这里的跟踪定义是:能狗将你收集的数据与其他公司 App 收集的数据相关联,比如使用了 IDFV 也算跟踪,。
只是这里我有个疑问,如果我不把 ATT 的相关收集域名放到 NSPrivacyTrackingDomains
列表,其实 Apple 是审核是不是也无法明确发现呢?那么我不做是不是也是可以?
第三方 SDK 签名
本次跟随隐私清单而来的还有第三方 SDK 签名,这是我们以前一直忽略的东西,而 Apple 的目的也很直接,就是希望通过签名认证的方式来确保 SDK 不会被篡改 ,简单的说就是 SDK 发布时带有 _CodeSignature
。
需要签名,肯定是 SDK 包含了二进制依赖,没有二进制依赖其实并不需要考虑签名问题。
这里的签名其实分 Apple Developer Program 签名和自签名,如果你的 SDK 使用的是 Apple Developer Program ,简单来说就是可以得到更安全可靠的认知加持,安全性拉满,但是如果你采用的是 codesign 自签名,那么也不是不行,就是你自己保管好签名认证的方式。
shell
echo "Build Archive Device Slice"
xcodebuild clean archive -sdk iphoneos -destination 'generic/platform=iOS'[...]
echo "Build Archive Simulator Slice"
xcodebuild clean archive -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' [...]
echo "Create XCFramework"
xcodebuild -create-xcframework [...] -output <YOUR.xcframework>
echo "Codesign XCFramework"
codesign --timestamp -v --sign "<YOUR CERTIFICATE (ABCXYZ)>" "<YOUR.xcframework>"
经过签名认证后的 SDK ,在 Xcode15 会显示对应的 Signature 信息,如果一旦发现本次前面和上次不一致,那么Xcode 就会让编译失败并弹出警告。
当然,如果 SDK 的签名变更符合你预期,比如 SDK 维护人员发生了变化,从而导致证书发生变化,那么你也可以通过接受变更的方式也同意更改。总的来说目前这个阶段签名并不是一定要官方,甚至不一定就强制要签名,Apple 「鼓励】所有 SDK 使用签名,但影响隐私的 SDK 一定要签名,特别是前面提到列表里的。
目前来说,App Store 已经开始检查最近提交的 App 是否包含影响隐私的 SDK ,如果影响隐私的 SDK 不包含签名和隐私清单,Apple 将向应用开发人员发送提醒邮件,包括提供必要理由的 API 部分,最后会在 2024 年春季开始要求审核。
更多官方资料:
- developer.apple.com/documentati...
- developer.apple.com/documentati...
- developer.apple.com/documentati...
Flutter
那本次隐私清单对于 Flutter 来说,最大的影响就是大量 Flutter Plugin 被挂上了 Apple 官方的用户隐私产生重大影响的第三方 SDK 列表,因为上了列表,就被认定为是影响隐私的 SDK,那么隐私清单是跑不了的了,另外如果有二进制依赖还需要满足签名条件,所以这对于 Flutter 来说在 iOS 17的适配上无疑「雪上加霜」,可以看到目前官方开了很多关于官方 package 的 iOS 隐私清单适配 issue 。
对于 Flutter Engine 部分,其实官方适配隐私清单难度并不大,目前调查完 iOS 上 Flutter Engine 主要需要适配的有:
-
File timestamps :
C617.1
用于FlutterAppDelegate.mm
里的应用状态恢复0A2A.1
用于实施相关File
包装器
-
System boot time:
35F9.1
用于各种事件计时和经过时间的计算
目前官方在 #131494 已经表示,会将 PrivacyInfo.xcprivacy
包含在未来 Flutter.framework
中,所以这部分并不需要担心。
而在 flutter/packages 部分目前却是有棘手的问题,例如 shared_preferences
,它被列入影响隐私 SDK 列表的原因是因为 NSUserDefaults
,但是 shared_preferences
它本身只是 API 的封装,为了方便 Flutter 开发者调用,它本身是不知道如何/为什么使用它,在 SDK 层面很难在隐私清单描述给出所谓的「收集原因」。
另外第三方 Plugin 也可能使用 shared_preferences
,但是 App 开发并不知道它用来做什么,那么如果让 pub 的 Plugin 能描述好隐私清单的内容?
目前官方文档提及:
If you use the API in your third-party SDK's code, then you need to report the API in your third-party SDK's privacy manifest file
这听起来像是只要拥有它的代码就需要标记出它的原因,但是实际上 Plugin 不知道该层存储的数据是什么或如何被使用。
对于 "收集" 的定义目前很模糊,类似的还有 webview_flutter
,webview_flutter
本身不收集任何内容,但是App 可以用来 webview_flutter
收集浏览历史记录,然后这如何在 SDK 的隐私清单里去体现?
感觉目前的 iOS 要求没有很明确,适配方向不够清晰。
最后,目前文档说他们「鼓励」每个人都采用该清单,而对于在隐私列表的 SDK 看起来是强制的,但是如何选择这些 SDK 的规则目前也看不到,所以只能等待后续和 Apple 的沟通回复。
更多进度可见:
最后
总结一下,本次 iOS 隐私清单主要覆盖的有:
- SDK 提供隐私清单的数据收集类型、使用描述和用途
- 必要理由的 API 需要提供使用"代码"和原因
- ATT 跟踪添加域名区分
- 第三方 SDK 签名
这里面 SDK 提供的隐私清单,我个人理解:
- 首要是用来给 App 打包后,通过导出的 pdf 参考管理隐私标签
- 其次对于必要理由的 API 的使用附带使用说明
- 关于 ATT 收集相关的数据域名添加到清单进行区分,至于你不写是否会被抓住不好说
- 第三方 SDK 签名不是强制,也可以自签名,前提是你不是在官方影响隐私 SDK 列表中
- 如果你 SDK 没有二进制,没有使用必要理由的 API,理论上其实甚至可以不管什么隐私清单和签名,因为你正常也进不去官方的影响隐私 SDK 列表。
本次更多是探讨,因为官方的描述和文档内容上其实并不严谨,甚至有很多模糊的地方。