F-Droid 仓库索引签名与防篡改机制
仓库索引是什么
每个 F-Droid 仓库都有一个索引文件(通常是 index-v1.jar),它是该仓库的「目录清单」:
- 列出该仓库中所有应用的元数据
- 每个应用的每个版本都有详细记录
- 包含 APK 的下载 URL、签名、哈希值、大小等信息
- 索引文件本身被 GPG 签名
F-Droid 客户端同步仓库时,首先下载这个索引文件,然后根据索引内容去下载具体的 APK。
索引签名如何防篡改
假设一个攻击者想破坏 F-Droid 的信任链。他可以尝试:
场景一:替换仓库服务器上的文件
攻击者入侵了 f-droid.org 服务器,替换了某个 APK 文件。结果:
- F-Droid 下载该 APK
- 验证 APK 的签名哈希 → 哈希值和索引中记录的不一致
- F-Droid 拒绝安装
攻击者也需要同时篡改索引文件——但索引文件有 GPG 签名,攻击者没有私钥,签不出合法签名。所以索引改不了,APK 替换也就无效。
场景二:中间人攻击
攻击者在网络中拦截了 F-Droid 的仓库请求,返回了伪造的索引文件。结果:
- F-Droid 下载伪造的索引文件
- 用内置公钥验证 GPG 签名 → 签名不匹配
- F-Droid 拒绝同步,显示「仓库签名无效」
场景三:DNS 劫持
攻击者劫持 DNS 请求,让 f-droid.org 解析到自己的服务器。结果和场景二一样——伪造的索引过不了 GPG 签名验证。
官方仓库的镜像
F-Droid 官方仓库(f-droid.org)有多个全球镜像节点:
- 主服务器:德国
- 镜像 1:美国
- 镜像 2:亚洲
- 更多镜像通过 CDN 自动分发
所有镜像节点使用完全相同的签名密钥——这就是为什么添加第三方仓库时需要单独确认密钥信任。镜像节点不能独立签名,它们只是分发主仓库的内容。
多仓库的安全隔离
F-Droid 可以同时连接多个仓库(官方 + IzzyOnDroid + Guardian Project 等),每个仓库有自己的签名密钥。当添加新仓库时:
- F-Droid 下载新仓库的索引
- 显示新仓库的签名指纹
- 让你确认是否信任这个密钥
- 确认后才会把该仓库的应用纳入搜索范围
重要:添加第三方仓库是一个安全决策。不要随便从网上搜仓库地址添加——确认来源可信。
仓库刷新频率
F-Droid 客户端默认每天刷新一次仓库索引。你可以手动触发刷新(设置 → 手动刷新)。
刷新频率不需要太高——F-Droid 官方仓库每周新增/更新几十个应用,每天查一次足够及时。
索引文件的格式
索引文件内部是一个 JSON 结构(index.json),包含:
{
"repo": {
"name": "F-Droid",
"address": "https://f-droid.org/repo",
"timestamp": 1728950000,
"apkversion": 11,
"apps": [
{
"packageName": "org.fdroid.fdroid",
"name": "F-Droid",
"summary": "Free and open source Android app store",
"categories": ["System"],
"versions": {
"2000051": {
"versionName": "2.0.1",
"hash": "sha256:abc123...",
"sig": "abc123def456...",
"apkName": "org.fdroid.fdroid_2000051.apk"
}
}
}
]
}
}
F-Droid 客户端和 fdroidserver 工具链都使用这个 JSON 格式。索引文件打包成 JAR 格式(带压缩),外部附带 GPG 签名文件(.asc)。
索引签名的局限性
GPG 签名可以防篡改,但不能防止索引本身的内容有问题——如果索引中某个应用的 APK URL 指向了恶意服务器,签名机制无法检测(虽然 F-Droid 会在下载 APK 后验证哈希)。
这也是为什么 F-Droid 有严格的准入审核——防的是「坏人混进来」,而签名防的是「坏人篡改已有内容」。