06 / 持久性
重装不换 ID,恢复出厂要分平台看
官方给了 Android 和 iOS 两张对照表,结论在恢复出厂这一项上正好相反。这是设计风控策略时最容易搞错的一处。
为什么重装影响不到它
传统做法是在设备上存一个随机 ID,应用一卸载就没了。指纹方案不存 ID,而是从硬件、系统属性、设备设置里推导出来,所以重装和清数据都够不着它。
Android:几乎什么都不变
| 事件 | visitorId |
|---|---|
| 应用或设备重启 | 不变 |
| 清除应用数据与缓存 | 不变 |
| 卸载后重装 | 不变 |
| 恢复出厂设置 | 大多不变,少数机型例外 |
| 装在同一用户的多个 profile | 不变 |
| 装在同设备的多个用户账户 | 不变 |
| 应用被克隆(多开) | 不变 |
多开出来的副本拿到的是同一个 visitorId。所以判断多开不能靠"ID 不一样",必须用克隆应用检测那个单独的布尔信号。这一点搞反,多开就完全漏过去了。
iOS:恢复出厂就会换
| 事件 | visitorId |
|---|---|
| 应用或设备重启 | 不变 |
| 卸载后重装 | 不变 |
| 更换描述文件或签名证书,AppID 不变 | 不变 |
| 设备越狱 | 不变 |
| 开启锁定模式 | 不变 |
| 设置恢复默认值 | 不变 |
| 恢复出厂设置 | 变,生成新 ID |
差异的原因在第 05 章讲过:iOS 的主干是 IDFV 加 Keychain,恢复出厂把两者一起清空,没有别的东西能续上。Android 靠的是硬件属性加服务端匹配,出厂设置清不掉硬件。
怎么判断设备刚被重置过
用恢复出厂检测信号,它返回两个字段:time 是最近一次恢复出厂的 UTC 时间,timestamp 是同一个值的 Unix 时间戳。
从未重置过的设备返回纪元时间,也就是 1970-01-01。浏览器发来的请求同样返回纪元时间。
- 需要 Android SDK 2.3.0 以上、iOS SDK 2.3.0 以上
- Android 在 2.10.0 之前误报较多,会把系统更新和厂商行为当成重置。2.10.0 起改了算法:如果一台设备升级 SDK 后从有时间戳变成纪元时间,说明它其实从来没被重置过
- iOS 模拟器里拿不到这个信号,Android 模拟器反而可以
真正有用的不是"重置过没有",而是重置发生在什么时间点。发生在交易失败或封号之后,通常是在清痕迹;短时间内多台设备集中重置,通常是团伙。
浏览器没有对照表,只有置信度
浏览器端不提供"变或不变"的表,因为它本来就不是确定性的。响应里带一个 confidence.score,例如 0.995,表示这次识别不是误判的概率。
官方对机制说得很直接:能拿到 Cookie 这类确定性属性时优先用确定性识别;隐身模式、清空存储、拦截三方 Cookie 的浏览器只能退回概率匹配,这时分数一定小于 1。
所以"清了数据还认不认得出"的答案是:认得出,但置信度会掉。用 visitorFound 区分首次访问和回访,用分数决定要不要加一道验证。官方给的阈值只是举例,比如低于 0.95 触发 OTP,没有标准值,要自己跑实验定。
能稳定多久
- 开源库算出的
fingerprint可能几周内就变 - 商业版
visitorId官方口径是数月到数年稳定,但明确说不保证永久 - 客户端缓存不要超过 24 小时,传入超过 86400 秒 SDK 会直接报错
结论很实际:别拿 visitorId 当主键。把它存在自己的账号体系旁边作为关联证据,并且预期它会漂移。
对抗现状
Safari 26 的进阶指纹防护换了思路:不封 API,而是在 canvas、WebGL 读取和 WebAudio 采样里注入噪声,并且每个标签页、每个会话的噪声值都不同,CPU 核心数每次读取都重新随机化。
但注入均匀噪声是已知的弱防御——多采样几次就能把噪声平均掉。Fingerprint 自己发过文章说明他们如何绕过 Safari 17 的音频指纹保护,恢复出的指纹唯一性和原来一样,代价是性能下降一点五到两倍。
这项防护默认是否在全部浏览模式下开启,社区有争议,需要自己在 Safari 设置里确认。