Android Application Security Essentials
上QQ阅读APP看书,第一时间看更新

Intents

Intents are Android's mechanism for inter-component communication. Intents are asynchronous so components fire them off and it is the onus of the receiving component to validate the incoming Intent's data and act upon it. Intents are used by the Android system for starting an Activity or Service, for communicating with a Service, to broadcast events or changes, for receiving notifications using pending Intents, and to query the Content Provider.

There are different mechanisms to handle Intents for each component. So, the Intents sent out to Activities, Services, and Broadcast Receivers are only sent to their respective counterparts by the Android system. For example, an event sent out to start an Activity using Context.startActivity() will resolve only Activities matching the Intent criterion. Similarly, a broadcast sent out using Context.sendBroadcast() will be received only by receivers and not by other components.

Before an Intent is sent out, it is important to check if there is a component to handle the Intent. If there is no component to handle the Intent, the application will crash. Matching Intents can be queried using the queryIntentActivities() method of the PackageManager class.

Note

Any rogue application can send an Intent to an exposed component. It is your component's responsibility to validate the input before acting on it.

Intents are basically serialized objects passed between components. This object contains some information used by the other component to act upon. For example, an Activity that logs in the user using their login credentials may start another Activity that loads up the books previously selected by the user using Context.startActivity(). In this case, the Intent may contain the user's account name that will be used to fetch books stored on the server.

An Intent object contains the following four kinds of information:

  1. Component Name: A Component Name is required only in case of an explicit Intent. It has to be a fully qualified classname if communicating with an external component or just the classname in case of an internal component.
  2. Action String: An Action String is the action that should be performed. For example, an Action String ACTION_ CALL initiates a phone call. A broadcast action ACTION_BATTERY_LOW is a warning to applications about low battery.
  3. Data: This is the URI of the data along with the MIME type. For example, for ACTION_CALL, the data will be of type tel:. Both data and the type of data go hand in hand. In order to work on some data, it is important to know the type so that it can be handled appropriately.
  4. Category: The Category provides additional information about the kind of Intents a component can receive, thereby adding further restrictions. For example, the browser can safely invoke an Activity with a Category of CATEGORY_BROWSERABLE.

Intents are asynchronous so no result is expected. In case of Activities, Intents can also be used for starting an Activity for result. This is done using Context.startActivityForResult() and the result is returned to the calling Activity using the finish() method.

Intents used for broadcasts are usually announcements about an action that just happened. Broadcast Receivers register to listen to such events. Some examples include ACTION_PACKAGE_ADDED, ACTION_TIME_TICK, ACTION_BOOT_COMPLETED. In this scenario, an Intent works like a trigger for some action to be performed once an event takes place.

Note

Do not put any sensitive information in the Intent object. Use another mechanism such as a Content Provider that can be protected by permissions to share information between components.

The receiving component gets extra information attached to the Intent class using getIntent().getExtras(). A secure programming practice requires that this input be validated and vetted for accepted values.

Explicit Intents

A component can send a targeted Intent to only one component. For this to happen, a component should know the fully qualified name of the target component. An Activity in Application A sending an explicit Intent to an Activity in Application B can be shown graphically as follows:

For example, an Activity can explicitly communicate with an internal Activity called ViewBooksActivity using the following code:

Intent myIntent = new Intent (this, ViewBooksActivity.class);
startActivity(myIntent);

If ViewBooksActivity is an external Activity, the component name should be a fully qualified name of the class. This can be done as follows:

Intent myIntent = new Intent (this, "com.example.android.Books.ViewBooksActivity.class");
startActivity(myIntent);

Since intents can be intercepted by any application, if the component name is available, it is best to call the component explicitly.

Implicit Intent

If the fully qualified name of the component is not known, the component can be called implicitly by specifying the action that a receiving component needs to do with it. The system then identifies components that are best suited to handle the Intent by matching the criterion specified in the Intent object. An illustration of an implicit Intent is shown as follows. An Activity in Application A sends out an Intent, and the system searches the relevant components (based on their Intent Filters and permissions) that can handle such an Intent.

The following are some examples of implicit Intents:

// Intent to view a webpage
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));

// Intent to dial a telephone number
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:4081112222"));

//Intent to send an email
Intent intent = new Intent(Intent.ACTION_SEND);
emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"me@example.com"});
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Hello Intent!");
emailIntent.putExtra(Intent.EXTRA_TEXT, "My implicit intent");

Intent Filter

For a component to be resolved by the system, it needs to be declared in the manifest file with appropriate identifiers. This task is done using the Intent Filters. Intent filters are defined for activities using the <intent-filter> subtag of the <activity>, <service>, or <receiver> declaration. While resolving an appropriate Activity for an Intent, the system considers only three aspects of an Intent object. These are action, data (both URI and MIME type), and category. All these Intent aspects must match for a successful resolution. A component name is used only for explicit Intents.

Intent Filters must contain the <action> subtag and may contain <category> and <data>. Some examples of the <intent-filter> declarations are as follows.

An Activity that is the starting point of the application is identified with these tags:

<intent-filter>
  <action android:name="android.intent.action.MAIN" />
  <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

An Activity that lets user request data of the type book can be defined as follows:

<intent-filter>
  <action android:name="android.intent.action.GET_CONTENT" />
  <category android:name="android.intent.category.DEFAULT" />
  <data android:mimeType="vnd.android.cursor.item/vnd.example.book"/>
</intent-filter>

Intent Filters are not security boundaries and should not be relied upon for security. Intent Filters cannot be secured with permissions. Moreover, any component with Intent Filters becomes an exported component and any application can send Intents to this component.

Pending Intent

In case of Intents, the receiving application executes code with its own permission as if it is a part of the receiving application. In case of a pending Intent, the receiving application uses the original application's identity and permissions and executes the code on its behalf.

Thus a pending Intent is a token an application gives to another application so that the other application can execute a piece of code with the original application's permissions and identity. A pending Intent will execute even if the sending application process is killed or destroyed. This property of pending Intents can be used beautifully to send notification to the originating application once an event has happened. Pending Intents can be either explicit or implicit.

For additional security, so that only one component receives the Intent, a component can be baked into the Intent by using the setComponent() method. By default, a pending Intent cannot be modified by the receiving component. This is good for security reasons. The only part that the receiving component can edit is extras. The sender can, however, set flags to explicitly enable receiving components to edit PendingIntent. For this to happen, the sender sets rules for using the fillIn(Intent, int) method. For example, if the sender wants to let the receiver overwrite the data field, even if it is set, then the sender can set FILL_IN_DATA=true. This is a very sensitive operation and should be done with care.