Safe Haven 中文文档 下载 App

Safe Haven 签名锁定机制,防止 APK 被偷偷替换

为什么要锁签名

安卓系统在安装时会检查同包名应用的签名是否一致——签名变了就拒绝覆盖安装。这是安卓的基础安全机制。但这个机制有一个盲区:如果开发者合法轮换了签名密钥,或者攻击者替换了 APK 但签名变了,用户在安装前是看不到签名变化的。

Safe Haven 的签名锁定(Signing-Key Pinning)把这个检查前置到了上架流程里。它不只是让安卓系统去检查,而是在服务端就记录下每个应用的签名哈希,之后每次复扫都比对,任何变化都会被记录和标记。

工作流程

第一步:首次扫描时锁定签名

任何一个新应用或新版本的 APK 进入 Safe Haven 扫描管线时,扫描器会:

  1. 从 APK 里提取签名证书
  2. 计算证书的 SHA-256 哈希(整个证书,不是 APK 文件)
  3. 把这个哈希值存储到应用条目的 signingKeyHash 字段里

这个存储只做一次——首次扫描哪个版本,就以那个版本的签名哈希作为基准。

第二步:每次复扫都比对

Safe Haven 有定期复扫管线,对已上架应用每轮重新扫描时:

  1. 提取当前 APK 的签名哈希
  2. 从数据库读出应用条目的历史基准哈希
  3. 做比对

一致 → 一切正常,更新扫描时间戳。

不一致 → 把 signing_key_changed 标志置为 true,这个标志会在应用条目的 trust metrics 里体现出来。标志会一直保留,直到管理员手动处理。

第三步:管理员处理变更

当签名变更标志被置位后,管理员有两个处理选项:

选项 A:合法轮换密钥,手动更新哈希

开发者可能因为密钥丢失、证书过期等原因合法轮换签名密钥。管理员通过专用 endpoint 覆盖应用条目的 signingKeyHash,把基准换成新的哈希值。

每次覆盖操作都会被记录到审计日志里,包含:

选项 B:可疑变更,标记为 retired 或 rejected

如果签名变更不是来自合法轮换,扫描管线可能检测到其他可疑迹象(比如哈希变了 + 文件大小变了 + manifest versionCode 没变),管理员可以将该版本标记为 retired(已退役)或 rejected(拒绝),并触发进一步调查。

安卓原生签名机制和 Safe Haven 的关系

安卓系统在安装时做签名比对:同包名 + 同签名 = 允许覆盖;同包名 + 不同签名 = 拒绝。

Safe Haven 的签名锁定和这个机制是两层检查,不冲突:

层级做了什么什么时候做
Safe Haven 服务端比对签名哈希,变更时标记上架前 + 定期复扫(用户看得到)
安卓系统比对签名证书,不同就拒绝安装时(用户看不到哈希值)

Safe Haven 的额外价值是把签名信息公开给用户——你在安装前就能在详情页上看到"上次扫描的签名哈希",甚至可以自己对照本地已安装版本的签名,发现签名早就变了。如果 Safe Haven 没有这个机制,你直到安装失败才会知道签名有问题。

官方客户端的签名指纹

Safe Haven 自己的客户端(com.colourswift.safehaven)由 ColourSwift 官方签名,SHA-256 证书指纹:

9c67f4224888f60e093cf7eab9b194e6d4cd73bb11313638c47b17f0d5f34ec4

如果你从非官方渠道下载了 Safe Haven 的 APK,可以用 Android SDK 的 apksigner verify --print-certs 命令比对。

F-Droid / IzzyOnDroid 来源的特殊情况

F-Droid 同步进来的 APK 是用F-Droid 自己的签名密钥重打包的,不是开发者原生签名。所以:

IzzyOnDroid 来源保留开发者原生签名,所以和开发者自己提交的包签名通常一致。

签名锁定的局限

签名锁定期防得住被动篡改(比如有人换包但签名没换掉),但防不住主动滥用(比如开发者自己用恶意密钥签了新版本发布)。后者只能靠扫描管线和社区监督来发现。

签名锁定期也不处理非签名层面的篡改——比如攻击者在 APK 里偷偷加了跟踪器但没换签名。这种情况 VX-TITANIUM 引擎的字节结构分析可能检出,但不保证。