Sanitize All The Extras!

Earlier this year, I blogged about not having an accidental API.

Google themselves were caught out for having an accidental API, in the form of App Ops. Originally, this was a simple accidental API, in that they exported an activity for app ops. However, they had a more subtle accidental API, now revealed to be a more general security flaw related to the use of Intent extras. For those of you, like me, who were wondering about the new isValidFragment() we had to implement on PreferenceActivity, it relates to this problem.

Tactically, do not export a PreferenceActivity.

Strategically:

  • Do not export activities unless you want third parties to invoke them

  • Make sure that your extras make sense, on all of your activities, as for exported activities, those extras are part of your API

PreferenceActivity supports extras to load specific PreferenceFragments into the activity. This is used heavily by the Settings app, to allow apps to drive straight into particular screens (actually fragments). Unfortunately, there was no logic in PreferenceActivity to ensure that only those fragments that were supposed to be externally reachable were loaded via these extras — hence, the addition of isValidFragment(). So, a properly-crafted Intent can open any exported PreferenceActivity and launch any PreferenceFragment from it, in the absence of such defenses.

Usually, this would not be a security risk on its own, but if the fragments are also looking at extras, to configure their own behavior, being able to reach any fragment means that some fragments that were written assuming they were only internally reachable might be at risk. The article I linked to above cites one example, related to changing the device’s lock screen PIN or pattern.

As a reminder, an activity is exported if either:

  • The <activity> has android:exported="true", or

  • The <activity> has an <intent-filter> and does not also have android:exported="false"

The latter approach is an anti-pattern, as it can cause confusion with choosers, since non-exported activities will appear in a chooser if the activities match on the <intent-filter>. Generally speaking, only have activities with an <intent-filter> if they actually are to be used by third-party apps, such as the home screen, and get rid of <intent-filter> from everything else.

For those activities that you do export, typically via <intent-filter>, sanitize all the extras!. These are inputs, no different than parameters you might have on some method that a third party could call. Apply defensive programming techniques to make sure that the extras fit valid values (and combinations of values, where applicable) before using them.