4. 权限滥用风险分析:权限申请原则、最小权限检查
权限滥用,说白了一个 App 要了你根本不需要的东西。比如一个手电筒应用,非要读你的通讯录——这不扯吗?我在做 SDK 安全评估的时候,这种案例见得太多了。今天我们就聊聊权限申请的底线在哪,以及怎么用最小权限原则把风险降下来。
核心观点:权限不是越多越好。每多一个权限,攻击面就大一圈。这是我花了几次事故换来的教训。
4.1 权限申请的三条铁律
先问个问题:你开发一个天气 App,需要访问位置权限吗?可能大部分人说“需要,因为要定位城市”。但你再想想——用户手动选城市不就行了?其实很多权限,是我们可以绕过去的。
我给自己定了一个原则,写在下面这个表里:
| 原则 | 含义 | 我踩过的坑 |
|---|---|---|
| 必要性 | 该权限是核心功能不可替代的? | 一个相册扫图功能,我当初用了存储权限,其实用 SAF 框架完全不需要。 |
| 最小化 | 能不能只申请必要子集? | 位置权限有精准和粗略之分,我曾直接要了精准,后来发现粗略完全够用。 |
| 即用即申请 | 不要启动就弹窗,要用时才弹。 | 记得有个游戏启动就要麦克风权限,用户直接给差评。 |
小提示:每次添加权限前,先问自己“没有这个权限会怎样?”如果功能还能跑,那就别申请。
4.2 最小权限检查——怎么落地?
说完了原则,咱得动真格检查。手动看 Manifest 太累了,我一般用工具辅助。但你得理解背后的逻辑,不然工具扫出来你也不知道该不该修。
第一步:理清权限依赖图
每个 SDK 组件到底用了哪些权限?这不是只看 AndroidManifest 就行的,有时候 Native 代码里偷偷调用了系统 API 获取设备信息。我曾经接手一个第三方登录 SDK,声明只用 INTERNET,结果它自己偷偷读 IMEI——这个只能靠运行时 hook 抓出来。
第二步:对比功能边界
把权限列表拆开:必须的 vs 可选的 vs 冗余的。我习惯画一张权限覆盖图:
避坑指南:我曾经因为一个冗余的“读取短信”权限,整款 App 被应用商店拒审。后来排查发现是某个老旧模块残留的权限声明,合并代码时忘了删。
第三步:自动化扫描
工具层面,我推荐三个层次:
- 静态扫描:检查 AndroidManifest 声明 + 代码中 API 调用。可以用自定义 lint 规则。
- 动态监控:运行时抓取真实权限调用。AppTonic 或 Frida 脚本都可以。
- 隐私沙箱对比:对照厂商隐私政策,看权限是否都写了原因。
下面这段 Python 代码,是我平时快速扫描一个 APK 权限声明的小工具片段:
import zipfile
import xml.etree.ElementTree as ET
def extract_permissions(apk_path):
with zipfile.ZipFile(apk_path, 'r') as z:
if 'AndroidManifest.xml' not in z.namelist():
return []
# 解压后的 manifest 需先用 AXMLPrinter2 转成可读 XML
raw = z.read('AndroidManifest.xml')
root = ET.fromstring(raw)
ns = {'android': 'http://schemas.android.com/apk/res/android'}
perms = root.findall('.//uses-permission')
return [p.get('{http://schemas.android.com/apk/res/android}name') for p in perms]
permissions = extract_permissions('your_app.apk')
print("声明的权限有:", permissions)
嗯,注意这只是一个开头。真正分析时,你还需要把每个权限对应的 API 调用链找出来。
4.3 权限矩阵——一张图管住全局
我项目里习惯维护一张权限矩阵表。每次版本迭代都更新它。下面是一个简化版示例:
| 权限名称 | 功能模块 | 是否必要 | 是否已申请原因 | 备注 |
|---|---|---|---|---|
| CAMERA | 扫码登录 | 是 | 是 | 仅在扫码页弹窗 |
| ACCESS_FINE_LOCATION | 地图导航 | 可降级为粗略 | 否 | 待优化 |
| READ_CONTACTS | 邀请好友 | 否(可选功能) | 否 | 考虑移除 |
有了这个矩阵,你跟产品经理吵架的时候也有底气。我上次就靠这表说服对方,把两个非必要权限删掉了。
4.4 核心流程图——最小权限决策路径
下面是我自己画的最小权限检查流程图。每次接入新 SDK 或者改权限时,我都会按这个路径走一遍:
你看这个图,从需求分析一直走到矩阵形成。红色分支表示冗余权限直接移除,绿色分支保留后还要做子集最小化。我每次改权限,都能在这张图上找到对应环节,不落下。
4.5 实战:一个位置权限的优化案例
以前接手一个天气 App,它申请了 ACCESS_FINE_LOCATION。我一看代码,发现定位只用了一次——用户首次打开时获取城市。之后全在本地缓存里读。那为啥不直接用 ACCESS_COARSE_LOCATION?甚至用 IP 定位就够。我跟产品反馈后,他们同意改成 COARSE,并且只在首次启动时弹窗。用户体验和安全性都上去了。
我的习惯:每次接入新 SDK,我都会先跑一遍静态扫描,然后动态抓一次真实权限调用。对比两张表,差异就是风险点。
记住,权限滥用风险往往不是恶意代码导致的,而是开发者懒、历史包袱、或者沟通不清。你肯多花十分钟画一张权限矩阵,后面就能省下好几天应付审核的时间。我自己的经验告诉我——最小权限不是束缚,是保护你自己的铠甲。