2

I need to get the name of the app chosen by user fired with Intent.ACTION_SEND for analytic purposes. The name of app will be obtained through BroadcastReceiver.

It works until one day, the security engineer in our team informed us that all PendingIntent in the codebase must have PendingIntent.FLAG_IMMUTABLE to be secure.

The flag added breaks the existing functionality because intent?.getParcelableExtra<ComponentName>(Intent.EXTRA_CHOSEN_COMPONENT)?.packageName will always return null.

Is there anything I can do? PendingIntent.FLAG_MUTABLE is sadly not an option for me.

You can find same way of doing this from Android Documentation - Getting information about sharing

MainActivity.kt

const val PENDING_INTENT_REQUEST_CODE = 0x1000
const val THIRD_PARTY_SHARE_REQUEST_CODE = 0x1001
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btnShare.setOnClickListener {
            openThirdPartyShareDialog()
        }
    }

    private fun openThirdPartyShareDialog() {
        val thirdPartyShareIntent = Intent().apply {
            action = Intent.ACTION_SEND
            type = "text/plain"
        }

        val broadcastIntent = Intent(this, ThirdPartyAppBroadcastReceiver::class.java)

        val pendingIntent = PendingIntent.getBroadcast(this,
            PENDING_INTENT_REQUEST_CODE,
            broadcastIntent,
            getPendingFlagIntent()
        )

        startActivityForResult(Intent.createChooser(
            thirdPartyShareIntent,
            null,
            pendingIntent.intentSender
        ), THIRD_PARTY_SHARE_REQUEST_CODE)
    }

    private fun getPendingFlagIntent(): Int {
        var flags = PendingIntent.FLAG_UPDATE_CURRENT
        if (Build.VERSION.SDK_INT >= 23) {
            flags = flags or PendingIntent.FLAG_IMMUTABLE
        }
        return flags
    }
}

ThirdPartyAppBroadcastReceiver.kt

class ThirdPartyAppBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
    // packageName will always be null !
        val packageName =
            intent?.getParcelableExtra<ComponentName>(Intent.EXTRA_CHOSEN_COMPONENT)?.packageName 
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.flamyoad.broadcast_share"
    >

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Broadcastshare"
        >
        <receiver android:name="com.flamyoad.broadcast_share.ThirdPartyAppBroadcastReceiver" />
        <activity
            android:name=".MainActivity"
            android:exported="true"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
flamyoad
  • 519
  • 7
  • 15

1 Answers1

2

Turns out it's fine to remove PendingIntent.FLAG_IMMUTABLE if you're using explicit intent.

Android apps send messages between components using Intents. Intents can either specify the target component (Explicit Intent) or list a general action and let the operating system deliver the Intent to any component on the device that registers an Intent Filter matching that action (Implicit Intent).

PendingIntents are Intents delegated to another app to be delivered at some future time. Creating an implicit intent wrapped under a PendingIntent is a security vulnerability that might lead to denial-of-service, private data theft, and privilege escalation.

You can read more about it from Remediation for Implicit PendingIntent Vulnerability

flamyoad
  • 519
  • 7
  • 15