安装之前,APK 里到底有什么
APK 是一个附带声明的 zip 文件。在安卓运行它的任何一行代码之前,这个安装包已经说明了它自称是谁、支持哪些安卓版本、想要哪些权限、哪些部分可以被其他应用调用,以及由谁签名。这些都不需要安装任何东西就能看到。先读一遍,就能把一次盲目信任变成十分钟的检查——而这次检查往往已经足够做出决定。
为什么侧载的 APK 值得先看一眼
从官方商店安装的应用有商品页、评价记录和发布者页面。侧载的 APK——从厂商网站、镜像站、论坛帖子、聊天消息或旧备份下载——没有任何这样的背景,而安装提示也代替不了它。安卓安装界面只显示应用名称和图标,权限要到最后才出现,而且经过分组和简化,恰好出现在你最不可能停下来思考的时刻。应用一旦安装并启动过一次,可能已经拿到了被授予的权限并用过网络。提问最便宜的时机是在此之前,趁文件还只是一个文件。
这并不是反对侧载本身。厂商会直接分发正当应用,企业会用这种方式部署内部工具,旧版本有时也能解决真实问题。这里反对的是不看就装。用本站的 APK 分析器 在浏览器里解析安装包——文件不会被上传,也不会被执行——清单、权限、组件和签名证书会在一个视图里列出来。无论用什么工具,都保持这个顺序:先检查,后安装;一个经不起检查的应用,本身就是答案。
清单是应用自己的声明
每个 APK 都带有 AndroidManifest.xml,以二进制 XML 存储,它是应用对自己的声明。它写明包名——安卓使用的唯一身份,例如 com.example.app——以及数字形式的 versionCode 和给人看的 versionName。它还声明应用包含的组件、请求的权限,以及预期的硬件和软件特性。由于安卓会强制执行这份文件的许多内容,它比商店页面或下载页更难随手伪造:应用不能悄悄使用从未声明的权限,也不能冒充一个包名不同的应用的更新。
清单里的两个数字能说明不少维护状况。minSdkVersion 是应用能运行的最老安卓版本;下限很低,意味着要支持多年前的设备和安全模型,有时是必要的,有时只是继承下来忘了改。targetSdkVersion 是应用声称遵循的安卓行为级别。谷歌每年都会提高商店更新所要求的目标级别,因为更高的目标意味着应用接受更新的隐私和安全规则。一个侧载应用如果目标级别落后好几年,不一定是恶意的——但它在告诉你,它回避了或从未满足过那些新规则,你应该问为什么。
权限要这样读
安卓把权限分类,类别比名称更重要。普通权限在安装时自动授予——访问网络就是熟悉的例子——因为系统认为它们风险低。危险权限涉及敏感数据和操作,如通讯录、短信、精确位置、麦克风和摄像头,需要用户可以拒绝的运行时弹窗。签名权限只授予使用同一证书签名的应用或系统本身,因此第三方应用声明它也得不到任何好处。读权限列表时,先在心里把它们分进这几个类别,再去评判任何一行。
决定结论的是场景,而不是数量。手电筒要求读取短信,没有任何诚实的理由;短信应用要求同一权限,至少能解释——读取验证码,或者申请成为默认短信应用。银行应用可能合理地请求许多权限,而一个简单游戏只请求几个,却可能恰好包含那个不合场景的。所以不要只按权限数量给应用打分。对每一个危险权限,问一句:这个应用的哪个功能没有它就用不了?映射不到功能的请求就是发现的问题,也正是安装时摘要会藏起来的东西。
导出组件与它们带来的攻击面
应用由组件构成——活动(界面)、服务、广播接收器和内容提供者——清单会说明其中哪些是导出的,也就是设备上的其他应用可以访问。导出组件本身不是错误:分享目标、文件打开方式和深度链接,都是因为某个组件被有意导出并加以保护,在现代安卓中还需要显式声明。风险在于那些本不该成为公共入口的导出组件:任何应用都能启动的服务、任何应用都能触发的接收器,或是把应用数据交给任何来访者、前面没有任何权限检查的提供者。
有两个清单标志值得在任何正式版应用里直接查看。标记为可调试的应用可以被连接设备上的工具附加和检查;那是开发设置,把它带进发布版,是一个关于构建方式的危险信号。允许备份意味着应用的私有数据可以进入设备备份,这可能很方便,也可能把你以为只留在应用内的数据移到一个远不受控的地方。这两个标志都不能证明应用有害,但都很容易检查、在成品中很难自圆其说,而且正是下载页面上永远不会出现的那类细节。
签名证书:安卓的身份体系
每个 APK 都经过签名,签名证书是安卓最接近身份证明的东西。只有当新旧两个版本包名相同且签名者相同时,安卓才会把新版本作为更新安装在已有应用之上。这条规则默默做了很多工作:陌生人无法为你已经信任的应用推送木马化更新,因为他们没有原始密钥;而证书指纹——通常以 SHA-256 摘要显示——是核对连续性的实用方法。如果厂商公布了预期指纹,就拿来比对。本站的证书与密钥匹配器更一般地覆盖了证书和密钥的同一套指纹逻辑。
要清楚指纹能证明什么、不能证明什么。它证明这个文件由持有某个私钥的人签署,并且与另一个由同一密钥签署的文件相符或不符。它不能证明密钥持有者诚实、称职,甚至不能证明他就是证书主题字段里写的那家公司——那些字段是签名者自己填的。被重新打包的应用通常使用与原版不同的证书签署,所以指纹不符是强烈的停止信号;指纹相符则只是连续性信号。安卓里的身份就是密钥的连续性,仅此而已——请按这个标准对待它。
一份安装前检查清单
按固定顺序把各部分拼起来,检查只需几分钟。第一,身份:包名是否与你预期的应用相符,版本代码和版本名对这次要下载的发布是否合理?第二,维护:最低和目标 SDK 级别有多老,是否符合一个仍在维护的应用?第三,权限:列出危险权限,逐个映射到功能,遇到解释不了的就停下来。第四,攻击面:哪些组件被导出,可调试和备份是否开启?第五,签名:证书指纹是否与发布者公布的值相符,或至少与你已经信任的那个副本相符?把文件放进 APK 分析器,在一个屏幕上逐项过一遍这份清单。
然后分三档做决定,而不是两档。一切都能映射到功能、指纹也核对无误,就安装。大部分相符但有一项需要观察,就谨慎安装——放在测试设备、工作资料里,或不授予有疑问的权限。包名错误、指纹不符,或某个危险权限背后想不出任何功能,就放弃。放弃只损失一次下载;反方向可能损失一个账号。也请保持分寸:这和数据包捕获分析器的头十分钟一样,是分诊——在把一个文件放进你随身携带的设备之前,快速而诚实地读一遍它对自己的声明。
常见问题
清单读起来干净,就说明 APK 安全吗?
不能。清单是一份声明,不是一份行为报告。一个应用可以声明一套看似克制而完全合理的权限和组件,却仍然在代码里做坏事——向外回连、滥用被合法授予的权限,或在安装后下载新的行为。读清单是分诊:它能快速而廉价地发现身份不符、无法解释的权限、暴露的组件和错误的签名证书。它不是恶意软件结论,任何把清单读取变成绿色“安全”印章的工具,都声称了证据不支持的东西。
为什么同一个应用在不同工具里显示的权限不同?
通常有三个正当原因。第一,版本门控:权限可以带有 maxSdkVersion,只适用于某个安卓级别以下,不同工具对是否显示被门控条目的处理不同。第二,分组:安卓在弹窗时按组展示权限,而清单列出的是单个权限,所以分组视图比完整列表看起来短。第三,合并:打包进应用的库会带来自己的清单条目,构建时会被合并进最终文件——所以安装后的清单可能包含应用自身源码从未声明过的权限。出现差异时应去看是哪一个原因,而不是自动当作篡改的证据。
不安装任何东西就能检查 APK 吗?
可以——这正是浏览器本地分析器的意义。APK 作为文件被解析:二进制清单被解码,资源被读取以获取标签和字符串,签名证书被提取并计算指纹。应用代码永远不会被执行,任何设备上都不会安装东西;使用本站的分析器时,文件也不会被上传——解析发生在你的浏览器标签页里。如果某个检查服务要求你把 APK 上传到服务器,先问问自己:这个文件——可能是一个内部构建——到底该不该出现在别人的机器上。