您的应用是否提供了这样的 Service,当触发某些操作时,作为回调它会启动另一个应用的 Activity
?
比如,一个 Intent
接受了另一个 Intent
作为它的 Extra 参数,并将其作为参数通过 startActivity()
进行调用。
您是否知道,这种做法会让您的应用变得容易被攻击?
接下来,我将会解释使用这种方法带来的问题,并提供一个解决方案来让您的应用在更安全的前提下实现相同功能。
问题
我们期望这种类型的交互流程如下图所示:
流程图展示了一个启动回调 activity 的 Intent 如何作为一个扩展数据添加到一个启动 Service 的 Intent 中,然后使用此 Intent 启动回调 Activity
如上图所示,Client 应用为 ClientCallbackActivity
创建了一个 Intent
并将其作为 extra 添加到启动 Provider 应用的 ApiService
组件的 Intent
中。处理完启动 ApiService 的请求之后,Provider 应用使用 Client 应用创建的 Intent
去启动 ClientCallbackActivity
。
❗️这里需要注意的是 Provider 应用正在使用它自己的应用 Context
去调用 startActivity()
方法。这样的调用方式会产生两个不良的后果:
由于
ClientCallbackActivity
是被 Provider 应用从外部启动的,在清单文件中必须将其标识为exported
,这样不仅 Provider 应用可以启动 ClientCallbackActivity,设备上任何其他的应用也都可以启动。传递给 ApiService 的嵌套
Intent
可以被用来启动 Provider 应用内的任意 Activity,包括私有的,可能包含敏感信息的或者非导出的 Activity。
考虑一下这种场景: 如果调用方应用提供的 Intent
并不是启动调用方应用内的 Activity (比如 ClientCallbackActivity
),而是去启动 Provider 应用中的私有 Activity,可能会发生什么?
流程图展示了,如何通过精心构建出的 Intent 来启动 Provider 应用中的 ApiSensitiveActivity,即使它没有被标记为 exported,并且也不应该被其他应用启动。
由于使用了嵌套 Intent
,Provider 应用很难防范其他应用去启动应用内部私有的、有潜在敏感信息的 Activity。因为 Provider 应用直接使用 Intent 调用 startActivity() 方法,即使 ApiSensitiveActivity
未被标记为 exported,仍然可以启动它。
解决方案: PendingIntent
解决方案很简单: 与其接受一个 Intent
,Provider 应用可以变为接受 PendingIntent
。Provider 应用接受 PendingIntent 参数,不接受 Intent 参数。
PendingIntent
和 Intent
之间的区别在于 PendingIntent
只有在其被创建的 context 才能被处理。
流程图展示了如何从创建 PendingIntent 的 context 中对其进行处理,以防止攻击者调用 Provider 应用中未被标记为 exported 的 Activity。
由于回调是以 PendingIntent
的方式提供的,当 Provider 应用对其调用 send()
方法时,startActivity()
方法的调用会被当作是从 Attacker 应用发起的,由于 Attacker 应用并没有启动 ApiSensitiveActivity
的权限,系统会阻止此次调用,防止 ApiSensitiveActivity
被启动。
这样的做法对于 Provider 应用来说当然是有好处的,那对于我们自己的应用呢?由于我们提供的是 PendingIntent
,现在就可以将 ClientCallbackActivity
设置为私有的,非导出的 Activity 组件。这样的变化使得 Client 和 Provider 应用都拥有了更好的安全性。
如果您熟悉关于 notification 处理或 alarm 管理的 API,您会注意到,它们使用了 PendingIntents
去激活某项操作或者在应用内进行 alarm 提示。PendingIntents
可以认为是被创建它的应用所处理的,这就是系统使用 PendingIntents
而不是普通 Intent 的原因。
总结
使用 Intent
作为实现对 Activity
回调的机制,无论对 Provider 还是 Client 应用,都会给其带来安全风险。这是因为 Intent 总是在其被调用的应用内的 Context
中被处理的。这个 Context
有可能会启动 Provider 应用中任意非导出 Activity,并且还会强制 Client 应用导出需要接受回调的 Activity。
相反 PendingIntents
是在其被创建的 Context
中被处理,这不仅可以让 Provider 应用自由地使用它们,而不用暴露出任何非导出 Activity,还可以让 Client 应用指定任意的 Activity (包括非导出的) 来接受回调。