Activities
allow applications to call each other, reusing each other’s features
and allowing for replacement or improvement of individual system pieces
whenever the user likes. Activities are often run in their own process,
running as their own UID, and therefore don’t have access to the
caller’s data aside from any data provided in the Intent used to call
the Activity. (Note that Activities implemented by the caller’s program
may share a process, depending on configuration.)
Tip
The
easiest way to make Activities safe is just to confirm any changes or
actions clearly with the user. If simply starting your Activity with
any possible Intent could result in harm or confusion, you need to
require a permission to start it. An Intent received by an Activity is
untrusted input and must be carefully and correctly validated.
Activities cannot
rely on IntentFilters (the <intent-filter> tag in
AndroidManifest.xml) to stop callers from passing them badly configured
Intents. Misunderstanding this is a relatively common source of bugs.
On the other hand, Activity implementers can rely on permission checks
as a security mechanism. Setting the android:permission attribute in an
<activity> declaration will prevent programs lacking the
specified permission from directly starting that Activity. Specifying a
manifest permission that callers must have doesn’t make the system
enforce an IntentFilter or clean Intents of unexpected values, so
always validate your input.
The following code shows
starting an Activity with an Intent. The Activity Manager will likely
decide to start the web browser to handle it, because the web browser
has an Activity registered with a matching IntentFilter.
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse ("http://www.isecpartners.com"));
this.startActivity(i);
The following code
demonstrates forcing the web browser’s Activity to handle an Intent
with action and data settings that aren’t permitted by the IntentFilter:
// The browser's intent filter isn't interested in this action
Intent i = new Intent("Cat-Farm Aardvark Pidgen");
// The browser's intent filter isn't interested in this Uri scheme
i.setData(Uri.parse ("marshmaellow:potatochip?"));
// The browser activity is going to get it anyway!
i.setComponent(new ComponentName("com.android.browser",
"com.android.browser.BrowserActivity"));
this.startActivity(i);
If you run this code, you
will see that the browser Activity starts. However, the browser is
robust, and aside from being started it just ignores this weird Intent.
The
following code provides a sample AndroidManifest entry that declares an
Activity called “.BlankShoppingList”. This sample Activity clears the
current shopping list and gives the user an empty list to start
editing. Because clearing is destructive, and happens without user
confirmation, this Activity must be restricted to trustworthy callers.
The “com.isecpartners.ACCESS_SHOPPING_LIST” permission allows programs
to delete or add items to the shopping list, so programs with that
permission are already trusted not to wreck the list. The description
of that permission also explains to users that granting it gives an
applications the ability to read and change shopping lists. We protect
this Activity with the following entry:
<activity
android:name=".BlankShoppingList"
android:permission="com.isecpartners.ACCESS_SHOPPING_LIST">
<intent-filter> <action
android:name="com.isecpartners.shopping.CLEAR_LIST" />
</intent-filter>
</activity>
Activities defined
without an IntentFilter or an android:exported attribute are not
publicly accessible—that is, other applications can’t start them with
Context.startActivity(Intent intent). These Activities are the safest
of all, but other applications won’t be able to reuse your
application’s Activities.
Developers need to be
careful when implementing Activities, but also when starting Activities
as well. Avoid putting data into Intents used to start Activities that
would be of interest to an attacker. A password-sensitive Binder or
message contents would be prime examples of data not to include! For
example, malware could register a higher priority IntentFilter and end
up getting the user’s sensitive data sent to its Activity instead.
When starting an
Activity, if you know the component you intend to have started, you can
specify that in the Intent by calling its setComponent() method. This
prevents the system from starting some other Activity in response to
your Intent. Even in this situation, it is still unsafe to pass
sensitive arguments in the Intent (for example, processes with the
GET_TASKS permission are able to see
ActivityManager.RecentTaskInformation, which includes the baseIntent
used to start Activities). You can think of the Intent used to start an
Activity as being like the command-line arguments of a program (and
these usually shouldn’t include secrets either).
Tip
Don’t
put sensitive data into Intents used to start Activities. Callers can’t
easily require manifest permissions of the Activities they start, so
your data might be exposed.