Intent 重定向

OWASP 类别: MASVS-PLATFORM: 平台交互

概览

Intent 重定向是指攻击者可以在易受攻击的应用上下文中,部分或完全控制用于启动新组件的 intent 的内容。

用于启动新组件的 intent 可以通过多种方式提供,最常见的方式是作为序列化的 intent 放在 extras 字段中,或者 marshaled 成字符串再解析。参数的部分控制也可能导致相同结果。

影响

影响可能各不相同。攻击者可能会执行易受攻击应用中的内部功能,或者访问私有组件,例如未导出的 ContentProvider 对象。

缓解措施

一般来说,不要暴露与重定向嵌套 intent 相关的功能。在不可避免的情况下,应用以下缓解措施:

  • 正确清理捆绑的信息。务必记住检查或清除标志(FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION, FLAG_GRANT_PERSISTABLE_URI_PERMISSION, 和 FLAG_GRANT_PREFIX_URI_PERMISSION),并检查 intent 被重定向到哪里。IntentSanitizer 可以帮助完成此过程。
  • 使用 PendingIntent 对象。这会阻止您的组件被导出,并使目标 action intent 不可变。

应用可以使用 ResolveActivity 等方法检查 intent 被重定向到哪里。

Kotlin

val intent = getIntent()
// Get the component name of the nested intent.
val forward = intent.getParcelableExtra<Parcelable>("key") as Intent
val name: ComponentName = forward.resolveActivity(packageManager)
// Check that the package name and class name contain the expected values.
if (name.packagename == "safe_package" && name.className == "safe_class") {
    // Redirect the nested intent.
    startActivity(forward)
}

Java

Intent intent = getIntent()
// Get the component name of the nested intent.
Intent forward = (Intent) intent.getParcelableExtra("key");
ComponentName name = forward.resolveActivity(getPackageManager());
// Check that the package name and class name contain the expected values.
if (name.getPackageName().equals("safe_package") &&
        name.getClassName().equals("safe_class")) {
    // Redirect the nested intent.
    startActivity(forward);
}

应用可以使用类似于以下逻辑的 IntentSanitizer

Kotlin

val intent = IntentSanitizer.Builder()
     .allowComponent("com.example.ActivityA")
     .allowData("com.example")
     .allowType("text/plain")
     .build()
     .sanitizeByThrowing(intent)

Java

Intent intent = new  IntentSanitizer.Builder()
     .allowComponent("com.example.ActivityA")
     .allowData("com.example")
     .allowType("text/plain")
     .build()
     .sanitizeByThrowing(intent);

默认保护

Android 16 引入了一种默认的安全强化解决方案,用于防范 Intent 重定向攻击。在大多数情况下,正常使用 intent 的应用不会遇到任何兼容性问题。

退出 Intent 重定向处理

Android 16 引入了一个新的 API,允许应用退出启动安全保护。在默认安全行为干扰合法应用用例的特定情况下,这可能是必要的。

在 Android 16 中,您可以通过在 Intent 对象上使用 removeLaunchSecurityProtection() 方法来退出安全保护。例如:

val i = intent
val iSublevel: Intent? = i.getParcelableExtra("sub_intent")
iSublevel?.removeLaunchSecurityProtection() // Opt out from hardening
iSublevel?.let { startActivity(it) }

常见错误

  • 检查 getCallingActivity() 是否返回非空值。恶意应用可以为此函数提供空值。
  • 假设 checkCallingPermission() 在所有上下文中都有效,或者该方法在实际返回整数时会抛出异常。

调试功能

对于以 Android 12(API level 31)或更高版本为目标平台的应用,您可以启用一个调试功能,在某些情况下,它可以帮助您检测应用是否正在不安全地启动 intent。

如果您的应用同时执行以下两项操作,系统将检测到不安全的 intent 启动,并发生 StrictMode 违规:

  • 您的应用从未发送 intent 的 extra 中解包嵌套 intent。
  • 您的应用立即使用该嵌套 intent 启动应用组件,例如将 intent 传递给 startActivity()startService()bindService()

资源