10. Intent安全:隐式Intent的匹配风险、Intent重定向攻击、PendingIntent的安全使用

Intent 是 Android 组件通信的“信使”。

但信使也可能被劫持、篡改,甚至变成攻击者的武器。

这一章,我们聊聊 Intent 的三个核心安全风险:隐式匹配、重定向攻击、PendingIntent 滥用。

10.1 隐式Intent的匹配风险

隐式 Intent 不指定目标组件,而是通过 action、category、data 让系统匹配。

听起来灵活,但风险也在这里。

核心问题:攻击者可以注册一个与你的 Intent 过滤器完全相同的组件,拦截本该发给你组件的 Intent。

举个例子。你的应用发送一个隐式 Intent 打开某个文件:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file:///sdcard/doc.pdf"), "application/pdf");
startActivity(intent);

系统会弹出选择器,让用户选择用哪个应用打开。但如果攻击者注册了一个恶意 Activity,也声明能处理 PDF 文件,用户可能误点。

更隐蔽的是,攻击者可以注册一个优先级更高的 Activity,直接接管 Intent,用户甚至看不到选择器。

我的建议:能用显式 Intent 就别用隐式。如果必须用隐式,一定要用 setPackage() 限制目标应用包名。

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/pdf");
intent.setPackage("com.example.pdfreader"); // 限制包名
startActivity(intent);

我在项目中遇到过一个问题:某个第三方 SDK 用隐式 Intent 启动支付页面,结果被恶意应用拦截,用户支付信息被窃取。后来我们强制要求 SDK 升级,改用显式 Intent + 包名校验。

10.2 Intent重定向攻击

这个攻击手法更隐蔽。它利用的是 Intent 的“传递”机制

攻击流程大致是这样:

  1. 你的应用 A 通过 PendingIntent 或 startActivityForResult 启动攻击者的应用 B。
  2. 应用 B 在 Intent 中嵌入一个恶意的 Intent 对象(作为 extra 数据)。
  3. 应用 A 收到结果后,没有校验就直接用这个 Intent 启动其他组件。
  4. 恶意 Intent 被“重定向”到攻击者指定的目标,比如窃取数据的 Activity。

说白了,就是攻击者利用你的应用“代劳”启动恶意组件。

我曾经踩过这个坑:一个社交应用在分享功能里,接收用户传过来的 Intent,然后直接 startActivity(intent)。攻击者构造一个包含恶意 Intent 的分享请求,诱导用户点击,结果跳到了钓鱼页面。修复方案很简单:对接收到的 Intent 做白名单校验,只允许启动已知的包名和类名。

防护要点:

  • 对从外部接收的 Intent 做严格校验,不要直接 startActivity()。
  • 使用 Intent.parseUri() 解析时,设置解析标志,禁止携带危险参数。
  • 对 PendingIntent 的创建者做签名校验。

10.3 PendingIntent的安全使用

PendingIntent 是一个“令牌”,它允许其他应用以你的权限执行某个 Intent。

听起来方便,但用不好就是灾难。

最常见的错误:PendingIntent 的 Intent 是可变的(mutable)

Android 12 之前,PendingIntent 默认是可变的。这意味着接收方可以修改 Intent 中的 extra 数据、action、甚至目标组件。

攻击者拿到你的 PendingIntent 后,可以篡改 Intent,让它启动一个恶意 Activity,或者发送一条伪造的广播。

关键规则:如果不需要接收方修改 Intent,请使用 PendingIntent.FLAG_IMMUTABLE。

// 安全做法:不可变
PendingIntent pendingIntent = PendingIntent.getActivity(
    context,
    requestCode,
    intent,
    PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT
);

// 危险做法:可变(Android 12 之前默认)
PendingIntent pendingIntent = PendingIntent.getActivity(
    context,
    requestCode,
    intent,
    PendingIntent.FLAG_UPDATE_CURRENT
);

嗯,这里要注意:FLAG_IMMUTABLE 是从 Android 6 开始引入的,但直到 Android 12 才成为默认行为。如果你的应用还在支持低版本,一定要显式设置。

另一个常见问题:PendingIntent 的 Intent 包含敏感数据

比如你把用户的 token 放在 Intent extra 里,然后创建 PendingIntent 传给通知栏。通知栏的接收方(系统)虽然不会泄露,但如果 PendingIntent 被其他应用获取,extra 数据就暴露了。

我的习惯:PendingIntent 的 Intent 只放必要信息,比如一个 requestCode 或 ID。真正的敏感数据通过其他安全通道传递。

10.4 知识体系图

下面这张图总结了 Intent 安全的三个核心风险与防护策略:

Intent 安全核心风险与防护 隐式Intent匹配风险 Intent重定向攻击 PendingIntent滥用 攻击者拦截Intent 恶意应用注册相同过滤器 用户误点或自动接管 数据泄露 / 钓鱼 Intent被篡改重定向 攻击者嵌入恶意Intent 应用代劳启动恶意组件 权限提升 / 数据窃取 令牌被篡改或滥用 可变PendingIntent被修改 敏感数据通过extra泄露 以应用权限执行恶意操作 防护策略 显式Intent + setPackage() | 白名单校验 | FLAG_IMMUTABLE | 最小化extra数据 签名校验 | 不直接startActivity外部Intent | 使用安全通道传递敏感信息

10.5 总结

Intent 安全说白了就三件事:

  • 隐式匹配:能显式就别隐式,必须隐式就限制包名。
  • 重定向攻击:别信外部传来的 Intent,用之前先校验。
  • PendingIntent:用 FLAG_IMMUTABLE,别把敏感数据塞 extra 里。

你想想看,这些攻击手法其实都不复杂,但一旦中招,后果往往很严重。我在做安全审计时,见过太多因为 Intent 使用不当导致的数据泄露案例。嗯,记住一点:对 Intent 保持敬畏之心,它不只是组件通信的工具,也可能是攻击者的入口。