O'Reilly logo

Application Security for the Android Platform by Jeff Six

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. Component Security and Permissions

The Types of Android Components

Android apps are composed of one of more components, of which there are multiple types. Each component can be secured in a slightly different way, so before we start talking about how to do so, a quick review of the types of components should be helpful.

The component that most Android developers learn about first is the Activity. An Activity is analogous to a single screen displayed on the device to a user that is composed of what the user actually sees. When building an app, you create classes that derive from the Activity base class and typically contain one or more Views, which are objects that users can interact with in some way, such as reading text displayed within the View, clicking a button, and so on. In proper application development terminology, the set of Activities is your application’s presentation layer.

A Service is an Android component that is designed for background processing. These components can run when your app is not visible (that is, none of its Activities are being displayed to the user and are not active). Services typically either carry out some computations or update the data sources for your app. For example, if you were to build an email client app, a Service would run in the background to open up connections to the configured mail servers every so often, check for new mail, download it, and store new messages in the local database. Services can also be used to update Activities directly, to provide updates to the user directly via a Notification, or really for any other type of task that needs to occur in the background.

A Content Provider is a component designed to share data across apps. You can think of a Content Provider as the public interface to your databases, allowing other apps to connect and either run queries (retrieve data) or issue updates (store data). A Content Provider typically is used as a front end for a database created using the Android standard SQLite database system. As Content Providers are typically used to share data across apps, properly securing them so that appropriate apps can access specific data is critical; we will soon see how to accomplish this.

Content Providers are accessed using URIs of this form:

content://authority_name/path/id

The authority_name is specified when one declares a Content Provider in AndroidManifest.xml, and points to the Content Provider itself that will handle the reference (this is usually the full, all lowercase name of the implementing class). The path can be any number of segments, from zero on up, and is used by the Content Provider to find the data in question. In a basic implementation, the path would simply be the name of the table that the data is in. The id refers to a particular item, such as an email message stored by the Content Provider. Thus, a URI into a Content Provider used by an email client app to store messages may look like:

content://com.example.testapps.test1.mailprovider/messages/inbox/155

As we will see later, this URI-based method of addressing data within Content Providers is key to properly securing the data contained within.

A Broadcast Receiver is a type of component that listens for system messages called Intents. An Intent can be thought of as a request for a certain action to take place. Apps can create Intents and either send them directly to a specific component (usually an Activity or a Service) or broadcast them system-wide to all apps that are running. A Broadcast Receiver is a component that can receive these systemwide broadcasts and act upon them; it can choose to listen for all broadcast Intents or set up filters so that it receives only Intents for the specific actions it cares about (and would, presumably, take action upon). As with most broadcast systems, more than one Broadcast Receiver can receive, and act upon, a single Intent.

That, in a nutshell, is the selection of components that you can create to form an Android app. Some apps may be very simple, composed only of a few Activities. Some apps require background processing and will require a Service component, or components. Apps that store and maintain data may use Content Providers to make those databases accessible to other apps. Some apps may also choose to implement a Broadcast Receiver, if they want to perform certain actions in response to requests from other apps. Using this combination of components, apps ranging from the very simple to the very complex can be created. For each component, care should be taken to make sure that the component can be used as it is intended, and no more. Let us now turn to the ways that components can be locked down and secured.

Intercomponent Signaling Using Intents

Intents are the primary way for apps to send messages to other apps (and can also be used to send messages to other components within the same app). To send a message, an app creates an Intent object that signifies that it wants a certain action to take place. This Intent object can specify a specific Activity or Service that you want to start up, or it can specify a specific piece of data. In the later case, the Intent can either specify which components should perform an action on that piece of data, or just ask that someone should. This means that Android allows for Intents that have specific recipients (the Activity or Service that it wishes to interact with) as well as Intents that are broadcast throughout the system to any components that may be listening. We’ll soon see how this can be controlled, and the relevant application security topics that arise.

One of the most common uses of Intents, and where most developers first encounter them, is having one Activity start another Activity. To do so, create an Intent with the name of the Activity you want to start and then pass that Intent to the calling Context to process. For example, for the current Activity to start up the mainActivity Activity, the following code could be used:

Intent actIntent = new Intent(myContext,
	com.example.testapps.test1.mainActivity.class);
myContext.startActivity(actIntent);

This code creates an Intent that specifies which Activity you want to start by providing its full class name. This is known as an explicit Intent. As we just discussed, you can create an Intent that does not specify which class should perform the desired action; this is known as an implicit Intent. In that case, the Intent is created with a specific action to be performed and most of the time, the piece of data that is to be acted upon. For example, if you want to display an image file, and you did not want to specify a specific app to do so, you would create and invoke the Intent like this:

Uri actURI = Uri.parse(pathToSpecificImageFile);
Intent actIntent = new Intent(Intent.ACTION_VIEW, actURI);
myContext.startActivity(actIntent);

When you issue this implicit Intent, the Android system will determine at run time which app should be invoked to handle the action specified in the Intent: the viewing of the image file located at the specific URI. The question then becomes: how does Android know which components should be used to perform a certain action specified in an Intent? The answer is the use of Intent filters. Each Activity, Service, and Broadcast Receiver can specify one or more of these filters, each of which specifies a type of action that they can process. For example, if we had an Activity with the ability to display GIF image files and wanted to expose that ability to other apps, we could set up an Intent filter on the Activity like this:

<intent-filter>
	<action android:name="android.intent.action.VIEW">
	<category android:name="android.intent.category.DEFAULT">
	<data android:mimeType="image/gif">
</intent-filter>

Note that in the case of an explicit Intent, where the component to receive the Intent is specified using its full class name, there is no checking of that component’s Intent filters; an explicit Intent is always delivered to its specified component.

So far we have seen both explicit and implicit Intents. We can also use the Intent mechanism to broadcast messages to all apps that are listening for them. This broadcast Intent mechanism is used by the Android system itself to announce certain events that multiple apps may want to know about and respond to with some action. For example, Android broadcasts an Intent when a telephone call is received on the device. This broadcast capability allows various components to share information in a very decoupled manner; when something that is of potential interest to multiple apps occurs, you can broadcast an announcement about it for anyone that is listening. As you can imagine, this is a powerful capability, and there are plenty of circumstances where you may want to control which components (of other apps on the device) can listen to your broadcasts, allowing them to be seen only by those apps that should have the rights to know about events. Let’s review broadcast Intents briefly.

You may recall that we may use a Service in our email client app example to run in the background and constantly download new messages from configured mail servers. In the event that a message is downloaded, we may want to let the other portions of our app know about this so that the user can be notified. Indeed, it may be a good idea to let multiple components know about this event, so that each can take their own appropriate action in response to the new message’s arrival. While the client’s user interface, if active, may prepare to display the message, another app may make a copy of it, and so on. In this case, we will use a broadcast Intent to inform anyone that is listening about this event.

First, we need to declare a new action string, to define the action that is being announced within the broadcast Intent. In this case, we will call it MESSAGE_RECEIVED. To do so, we simply define a new String, like so:

public static final String MESSAGE_RECEIVED =
	"com.example.testapps.test1.action.MESSAGE_RECEIVED";

Then, to actually create the Intent and broadcast it throughout the Android system to all listening components, we issue:

Intent bdctIntent = new Intent(MESSAGE_RECEIVED);
myContext.sendBroadcast(bdctIntent);

Once we run this code, the device will broadcast the Intent out to any components that have intent-filters that allow them to receive it. To configure a component to receive these broadcast Intents, we would add a very basic intent-filter to our component in the manifest:

<intent-filter>
	<action android:name="com.example.testapps.test1.action.MESSAGE_RECEIVED">
</intent-filter>

Note that in this example, we do not include any information in the Intent, such as the URI of the new message. We simply broadcast the information that a message has been received; it is up to the components that receive the broadcast to take action on this notification. In order to receive broadcasts, an app should implement a Broadcast Receiver component, a type specifically designed to receive broadcast Intents and perform some action in response.

Now that we have discussed Intents as the primary communication path between components, both those within one app and those belonging to separate apps, we can move on to securing the various components that make up Android apps. In the course of this discussion, always keep in mind the Principle of Least Privilege, each component should be able to do exactly what it needs to do in order to carry out its function, and nothing else.

Public and Private Components

At the most fundamental level of access control lies the Android concept of exporting. Each component, be it an Activity, a Service, a Content Provider, or a Broadcast Receiver can be public or private. If a component is public, components of other apps can interact with it (start the Service, start the Activity, etc.). If a component is private, the only components that can interact with it are those from the same app or another app that runs with the same UID (see Chapter 3 for more detail).

The exported attribute in each component’s declaration in the AndroidManifest.xml file determines whether the component is public or private. When exported is true, the component is public (and therefore exported to other apps), otherwise the component is private (not exported to other apps).

The default behavior depends on whether the component is likely to be used externally. In the case of Activities, Services, and Broadcast Receivers, the default depends on how the component is configured with regard to Intents. As we have seen, a component can specify an Intent filter that allows it to receive Intents from other apps to carry out tasks. As such, if a component specifies an Intent filter, it is assumed that you want the component to be accessed by other apps and it is, by default, exported and thus public. If, however, no Intent filter is specified for a component, the only way to send an Intent to it is to fully specify the component’s class name. Therefore, it is assumed that you do not want to make this component publicly accessible, so the default exported is false and the component is private.

For example, a Service is normally public if an Intent filter is specified in its declaration and private if not. However, we may want to make a Service public but not specify an Intent filter, so that only components that know its full class name can make use of it (and we would probably want to restrict which components can access it using permissions, which we will discuss later). In this case, we need to specify the exported attribute as true, overriding the default:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.testapps.test1">
 ...
    <service android:name=".MailListenerService"
              android:enabled="true"
              android:exported="true">
              <intent-filter></intent-filter>
    </service>
  ...
</manifest>

The default exported value for Content Providers is true. Because the primary purpose of a Content Provider is to share information between apps, it is assumed that these should be public and accessible by other apps.

Imposing Restrictions on Access to Components

Note that any component that is public (exported is set to true), can be accessed by any component in any app on the device. This global access is sometimes necessary: for example, the main Activity for an app is normally public and unrestricted so that the app can be started from anywhere. However, in many cases, components should be able to specify which components in other apps can access them. We will now go through the different types of components and discuss how to restrict access to them, so that the Principle of Least Privilege can be applied throughout a multi-component app.

Securing Activities

As we have discussed, Activities serve as the presentation layer for your app. Their security and permissions are pretty straightforward and just consists of who can start the Activity. To require a certain permission to start an Activity, you need to add the permission attribute to the specific Activity’s entry in AndroidManifest.xml. For example, to declare an Activity that requires the START_ACTIVITY1 permission, the entry in the manifest would look like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.testapps.test1">
 ...
    <activity android:name=".Activity1"
              android:permission="com.example.testapps.test1.permission.START_ACTIVITY1">
              <intent-filter>
                 ...
              </intent-filter>
    </activity>
  ...
</manifest>

If an app wanted to start this Activity, it would do so by creating an Intent specifying the desire to start the Activity and then call either Context.startActivity() or Activity.startActivityForResult(). If the caller has the specified permission (START_ACTIVITY1 in this case), both of these calls will succeed, resulting in the Activity being started. Without that permission, both will fail and throw a SecurityException.

Securing Services

Services are the background processing components in Android. It is possible to restrict who can interact with a service by applying permissions; this will affect any attempts to interact with the service by either creating it or binding to it. As Services typically perform some variety of processing that consists of updating databases, providing notifications of an external event, or performing some other task for the benefit of a component that will interact with the user, it is important to make sure that they are accessible only by appropriate consumers. To require a permission to create or bind to a service, simply add the permission attribute to the specific Service’s entry in AndroidManifest.xml. For example, to declare a Service that requires the BIND_TO_MAIL_LISTENER permission, the entry in the manifest would look like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.testapps.test1">
 ...
    <service android:name=".MailListenerService"
              android:permission=
                 "com.example.testapps.test1.permission.BIND_TO_MAIL_LISTENER"
              android:enabled="true"
              android:exported="true">
              <intent-filter></intent-filter>
    </service>
  ...
</manifest>

Note that in this example, we create a service that does not have an Intent filter, so it can be invoked only by an Intent that fully specifies the class name. We did this to imply a design where only our own apps would connect to this Service and therefore would know the full name of our Service. As this service does not have an Intent filter, we also needed to specify the exported attribute as true, as this defaults to false for components that do not specify Intent filters.

If an app attempted to interact with this service—starting it by calling Context.startService(), stopping it by calling Context.stopService(), or binding to it by calling Context.bindService()—the call would succeed if the caller has the specific permission (BIND_TO_MAIL_LISTENER). If the caller does not have this permission, the call would fail with a thrown SecurityException.

This capability controls who can interact with a service only at a very coarse level (you can control who can start, stop, and bind to a Service). You cannot restrict access permissions on a more fine-grain level using this mechanism, such as providing permission checks on specific callable methods of a bound service. For such flexibility, use the general capability of verifying permissions programmatically that we discussed in Chapter 3. You can call checkCallingPermission() to see whether the calling app has a specific permission at the entry point of any methods you think require permission checks over and above the permission configuration set for the overall Service. Of course, you can make such a check at any point in a method as well; doing so before performing a sensitive operation is the key.

Securing Content Providers

Content Providers are the standard way for apps to make their data available to other apps. As these components exist to share data among different consumers and suppliers, they raise the need for a more complex security model.

Unlike Activities and Services, Content Providers can specify two different required permissions: one for reading and one for writing. This allows apps to be configured using the Principle of Least Privilege and recognizes how common designs are where some apps should be able to read certain data, other apps should be able to write that data, and still others should not be allowed to access the data at all.

One common misconception about these permissions is that having the write permission automatically implies the read permission. The logic is that updating data (writing) is a more powerful permission than simply reading it and anyone that can write into a database should also be able to read from it. This is a fallacy, and the Android design, separating read and write permissions, acknowledges that. For example, consider an email client app (I really do seem to like this example when discussing component security, don’t I?). In our app, we may implement a Content Provider to store email we download from a mail server. A Service may periodically connect to that mail server, download new messages, and put them into our database by writing to that Content Provider. The Service clearly needs the permission to write to the Content Provider, but does it need the permission to read from it? No. In fact, it should not have that permission, as it is not needed for it to do the job it performs. Remember the Principle of Least Privilege: grant the permissions needed to get the job done and no more.

For example, to declare a Content Provider that requires separate permissions to read or write data, the entry in the manifest would look like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.testapps.test1">
 ...
 	<provider android.name="com.example.testapps.test1.MailProvider"
 			  android.authorities="com.example.testapps.test1.mailprovider"
 			  android.readPermission="com.example.testapps.test1.permission.DB_READ"
 			  android.writePermission="com.example.testapps.test1.permission.DB_WRITE">
 	</provider>
  ...
</manifest>

With Content Providers, permissions are first verified when you connect to the provider. When you do so, you can connect to it if you have either the read permission or the write permission. If you have neither, the connection request will fail with a thrown SecurityException.

Permissions are then checked when you attempt to interact with the Content Provider. Using its query() method requires the read permission, while using its insert(), update(), or delete() methods requires the write permission, as specified in the manifest declaration (DB_READ and DB_WRITE in this case). Again, note that having the write permission does not automatically give you the read permission; the two are separate and enforced independently. If you were to call one of these methods without the corresponding permission, the call would not succeed and a SecurityException would be thrown instead.

The ability to separate read and write permissions allows us to better control which apps can interact with our Content Providers and how they can do so. However, when permissions are enforced using these methods, they apply to all of the data within a given provider, which can sometimes be way too much. Let’s think about our email client app again. We want to allow only the user interface components of our app to access the underlying Content Provider that holds all of the email; we certainly do not want arbitrary other apps to be able to do so, as email is sensitive information. However, what about the case of attachments to email? If an image or sound file is attached to a message, the email client app needs to be able to invoke the appropriate media handler to properly present that attachment to the user, and this would require the media handling app to have read permissions into the Content Provider that the attachment is stored in. This is clearly not a good idea, as we would be granting these apps the ability to read all of the stored email.

The solution to this problem is to make use of URI permissions. The Android designers have recognized that developers may need to grant permissions to certain portions of a Content Provider’s database for a short period of time. The case of allowing a media app access to an attachment within the Content Provider when the user wants to display that attachment is an illustrative example of this (indeed, it is such a good example that it is the same one used in the Android documentation when it introduces URI permissions!). With this mechanism, any component that has permission to access a URI within a Content Provider can temporarily grant that access to another app/component. In the case of the email client app, the user interface app (which does have permission to the entire Content Provider with email and attachment content) could temporarily grant permission to the URI that contains an attachment to the image viewer that is being called upon to display the image, even though the image viewer has no permissions to access the Content Provider by itself.

In order to use the URI permission system, a Content Provider must be configured to allow this in the Android manifest. Because this system is used to extend permissions beyond what is normally configured, the mechanism is turned off by default. There are two ways to enable URI permissions: one that covers any URI contained within the Content Provider and another that covers only a subset of URIs. In order to enable temporary URI permissions globally within a Content Provider, set the grantUriPermissions attribute in the <provider> declaration to true, like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.testapps.test1">
 ...
 	<provider android.name="com.example.testapps.test1.MailProvider"
 			  android.authorities="com.example.testapps.test1.mailprovider"
 			  android.readPermission="com.example.testapps.test1.permission.DB_READ"
 			  android.writePermission="com.example.testapps.test1.permission.DB_WRITE"
 			  android:grantUriPermissions="true">
 	</provider>
  ...
</manifest>

Note that this configuration attribute does not actually grant those permissions simply by including this option; it merely allows a component that has the access already to temporarily grant those permissions later to another app.

This global configuration option allows the Content Provider to grant permissions to any accessible URI within it. It is entirely possible for a Content Provider to be interested only in granting temporary permissions to a subset of URIs. In our email client app example, we envisioned using this mechanism to temporarily grant media-handling apps access to attachments in our email database. It makes sense to store attachments in a different path within the database, separate from messages. For example, the URI to a mail message may look like this:

content://com.example.testapps.test1.mailprovider/messages/inbox/155

And the URI to an attachment may look like this:

content://com.example.testapps.test1.mailprovider/attachments/42

In this case, we may want to temporarily grant permissions to entries in the /attachments/ path, but never grant permissions to the /messages/ path. In this case, globally enabling URI permissions would violate the Principle of Least Privilege. In order to deal with this, we can omit the grantUriPermissions attribute and instead include separate <grantUriPermission> elements within the <provider> element. Each <grantUriPermission> element specifies a subset of the Content Provider for which it can grant URI permissions. For each such element, you include one of three possible attributes: path, pathPattern, or pathPrefix.

To specify a directory whose files can be accessed through URI permissions, use the path attribute in the <grant-uri-permission> element. For example, to allow URI permissions to be granted on the /attachments/ path, the relevant configuration would look like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.testapps.test1">
 ...
 	<provider android.name="com.example.testapps.test1.MailProvider"
 			  android.authorities="com.example.testapps.test1.mailprovider"
 			  android.readPermission="com.example.testapps.test1.permission.DB_READ"
 			  android.writePermission="com.example.testapps.test1.permission.DB_WRITE">
 		<grant-uri-permission android:path="/attachments/" />
 	</provider>
  ...
</manifest>

The path attribute is not recursive; if there is any subdirectory under /attachments/, no access is granted to files in that subdirectory. To specify a path that is a prefix (meaning that all URIs that fall under that path can be granted URI permissions), use the pathPrefix attribute in the <grant-uri-permission> element. For example, if we wanted to be able to grant URI permissions on messages within our database (any path that began with /messages/), we could configure the Content Provider like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.testapps.test1">
 ...
 	<provider android.name="com.example.testapps.test1.MailProvider"
 			  android.authorities="com.example.testapps.test1.mailprovider"
 			  android.readPermission="com.example.testapps.test1.permission.DB_READ"
 			  android.writePermission="com.example.testapps.test1.permission.DB_WRITE">
 		<grant-uri-permission android:pathPrefix="/messages/" />
 	</provider>
  ...
</manifest>

Finally, the pathPattern attribute can be used to supply a full path just like the path attribute does, but supports wildcards. Wildcards in this type of specification follow the format of using an asterisk (*) to match zero or more occurrences of the character that immediately precedes it. So to match zero or more of the letter A, you would use the sequence A*. You can also use a period (.) to match any single character. To match zero or more characters (any characters), you would use the two-character .* sequence. For example, if you wanted to be able to grant permissions to any URIs that contain the string public in them, write:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.testapps.test1">
 ...
 	<provider android.name="com.example.testapps.test1.MailProvider"
 			  android.authorities="com.example.testapps.test1.mailprovider"
 			  android.readPermission="com.example.testapps.test1.permission.DB_READ"
 			  android.writePermission="com.example.testapps.test1.permission.DB_WRITE">
 		<grant-uri-permission android:pathPattern=".*public.*" />
 	</provider>
  ...
</manifest>

So we now see that Content Providers can be configured so apps that already have access to them can temporarily grant those permissions to other apps. Once the Content Provider has been configured in this way, the app needs to actually grant those permissions. This is fairly easy, as it consists solely of setting the appropriate flag in the Intent created to call the new app. To temporarily grant read permissions to the app being called, set the FLAG_GRANT_READ_URI_PERMISSION flag. To temporarily grant write permissions to the app being called, set the FLAG_GRANT_WRITE_URI_PERMISSION flag. In the case of our email client app, an Intent would be created that specifies the URI of the attachment that we want to open. Then the FLAG_GRANT_READ_URI_PERMISSION flag would be set on the Intent. When this Intent is passed to Context.startActivity(), the image viewer Activity would be started, targeting the URI within the Content Provider. Because the appropriate flag was set, the image viewer app would have permission to access the specific URI in the Content Provider and all would function as intended. For example:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(uri, "image/gif");
startActivity(intent);

An alternative approach to specifying the flags in an Intent is to explicitly grant a specific app permissions on a Content Provider URI. Note that this is not the preferred way to pass permissions; the flags specified in the Intent is the standard approach. But to do the alternative approach in a program, you can use the Context.grantUriPermission() method, passing in the package name to which you wish to temporarily grant the permissions, the URI in question, and the Intent flags specifying which permission(s) you want to grant. For example, to grant another app read permissions to access a message within the email client’s database:

uri = "content://com.example.testapps.test1.mailprovider/attachments/42";
Context.grantUriPermission("com.example.testapps.test2", uri,
	Intent.FLAG_GRANT_READ_URI_PERMISSION);

Of course, if you are going to grant another app permission into a Content Provider in this way, you need to revoke that permission when access is no longer necessary (remember, this is intended to be a temporary grant of permission). To do so, call the Content.revokeUriPermission() method, passing in the URI and the flags. Note that this method will revoke all permissions temporarily granted to that URI, along with permissions to any other URIs on the same path that had permissions granted this way. For example:

uri = "content://com.example.testapps.test1.mailprovider/attachments/42";
Context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

This code will revoke granted permissions for /attachments/42. Had permissions been temporarily granted using this method to other items at the same path level, such as /attachments/22, those permissions would be revoked by this call as well.

Finally, it is also possible to programmatically check to see whether a specific app (if you know its process and UID) has been granted access to a specific Content Provider URI. Note that this mechanism only checks to see whether the specified process has been granted permission using one of the two mechanisms described in this section and does not consider broader, general access. Therefore you should rely on this approach only to check temporary URI permissions.

To check to see whether another app has the specific permissions, call checkUriPermission() and pass in the URI, the PID you want to check, the UID you want to check, and the flags you want to verify. For example:

int canProcess = checkUriPermission(uri, 377, 122, Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (canProcess != PERMISSION_DENIED)
{
	...
}
else
	throw new SecurityException();

This code will check to see whether the process running with PID 377 and UID 122 has been granted read access to the URI specified. If you are handling an IPC for another app, you can use a different approach to check whether that calling app has been granted the specified permissions. Note that this method works only if you are processing an IPC from another app; if you are not, it will always return PERMISSION_DENIED:

int canProcess = checkCallingUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (canProcess != PERMISSION_DENIED)
{
	...
}
else
	throw new SecurityException();

And that’s the word on Content Providers. These components require a longer discussion than the other components because they are the most tricky to secure. However, now that you understand the general concepts, it’s not really that hard. Content Providers use standard Android permissions to allow apps read and/or write access to them. Many situations require more fine-grained access, so URI permissions are available for apps to temporarily grant permissions to certain content within a Content Provider when necessary. Once you understand how the URI permissions work, it is possible to grant specific apps the specific permissions they need to specific content at the specific times they need them. This is the Principle of Least Privilege at its finest.

Securing Broadcast Intents

As you will recall from our earlier discussion, messages are commonly broadcast out to any app that is listening for them using Broadcast Receivers. We discussed our email client app example and how the Service that is constantly checking for new mail may choose to send out a broadcast Intent when a new message has been received, so that multiple components may choose to act upon this. In this case, we most likely want to limit the components that can receive such a broadcast, as we do not want to go announcing to the whole world that an email message has just come in.

The sender of broadcasts can choose to apply an Android permission to each broadcast it sends, that broadcast will be delivered only to those Broadcast Receivers that both have an Intent filter that allows them to receive it and the specified permissions that indicate they are authorized to do so. In the case of our Service example, we can restrict which Broadcast Receivers are allowed to receive our broadcasts by sending the broadcast only to those with a MSG_NOTIFY_RECEIVE permission that we create for this purpose:

Intent bdctIntent = new Intent(MESSAGE_RECEIVED);
myContext.sendBroadcast(bdctIntent,
	"com.example.testapps.test1.permission.MSG_NOTIFY_RECEIVE");

Note that in many cases, when a permission check fails, a SecurityException is thrown. When we lock down broadcasts in this manner, no SecurityException will be thrown if a Broadcast Receiver specifies that they should receive these broadcasts but they do not have the specified permissions. Indeed, since this code attempts to send the specified broadcast Intent to any Broadcast Receiver with a matching Intent filter, some of these receivers may have the specified permission and some may not; no feedback is returned to the component sending the broadcast Intent as to which succeeded and which failed.

This mechanism enables the sender of a broadcast to specify which receivers are allowed to receive it. It is also possible to do the reverse: to configure a Broadcast Receiver to accept incoming broadcast Intents only from senders that hold the specified permissions. To do this, simply specify a permission attribute in the <receiver> element in AndroidManifest.xml. For example:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.testapps.test1">
 ...
    <receiver android:name=".UIMailBroadcastReceiver"
              android:permission=
                 "com.example.testapps.test1.permission.MSG_NOTIFY_SEND">
		<intent-filter>
			<action android:name="com.example.testapps.test1.action.MESSAGE_RECEVIED">
		</intent-filet>
    </receiver>
  ...
</manifest>

This declares a Broadcast Receiver that listens for MESSAGE_RECEIVED broadcast Intents and accepts them only from senders that have been granted the MSG_NOTIFY_SEND permission. If a MESSAGE_RECEIVED broadcast Intent arrives from a sender without that permission, it will not be delivered to this Broadcast Receiver.

It is also possible to register a Broadcast Receiver programmatically, instead of in the AndroidManifest.xml file, by calling registerReceiver(). In this case, you can still apply a permission restriction, only allowing senders with that permission to send to the registering Broadcast Receiver. For example:

IntentFilter intentFilter = new IntentFilter(MESSAGE_RECEIVED);
UIMailBroadcastReceiver rcv = new UIMailBroadcastReceiver();
myContext.registerReceiver(rcv, intentFilter,
	"com.example.testapps.test1.permission.MSG_NOTIFY_SEND", null);

As you can see, broadcasts can be secured in either direction. Senders of broadcasts can configure them so that only receivers with specific permissions are allowed to receive them. Receivers of broadcasts can be configured to accept them only from senders with specific permissions. Using a combination of these two mechanisms, it is possible to set up a broadcast architecture where each component that should be notified about certain events is notified (and those that should not be notified about them are not), and each component accepts event notifications only from authorized senders.

Putting It All Together: Securing Communications in a Multi-Tier App

The Android permission system allows the various components that make up an app to set restrictions on what other components can access them. While some basic apps are composed of just a few Activities, larger and more sophisticated apps are typically composed of a combination of Activities, Services, Content Providers, and Broadcast Receivers. In addition, Android encourages the use of decoupled components through the Intent system, where multiple components belonging to different apps can work together to fulfill a desired function. This method of app design, thinking in terms of components instead of apps, can result in many advantages, but can also result in many apps receiving access to data and information that they should not have. As such, a proper security architecture must be part of any involved app design. As a developer, you must consider what type of data and what type of service your components are providing and who should be granted access to them. Once you have determined that, the actual application of a permissions scheme to implement it is very straightforward.

For Activities, you need to carefully consider who should be able to invoke/start the Activity. For the initial Activity that will be displayed to the user when your app starts, no permissions are required. For other Activities, apply permissions so that the Activities in question can be started only by others that have the correct permissions.

For Services, you need to consider who should be able to start them, stop them, and bind to them. Because Services run in the background, it is likely they are providing some service that other components of your app will access. If they are doing anything with sensitive data or performing any functions that should not be available to any app in the world, permissions should be applied so that only apps that should have access to their functions are allowed. In addition, if your Service supports binding and allows apps to make calls to methods for functions it can provide, you may want to add further programmatic checks within some of those methods, if there are different levels of sensitivity. For example, in the email client app, a certain permission may be necessary to access a method that checks when the most recent email message was received, while a separate permission may be necessary to update the mail server settings. In such a case, restricting the different methods with different permissions is clearly necessary.

For Content Providers, careful consideration must be taken in designing their permissions. Read and write permissions can be set on Content Providers and any components that require access to the provider should be granted only the permissions that they truly need to perform their jobs. Content Providers also include the ability to grant URI permissions—whereby an authorized component may temporarily grant permissions to another component that does not have any rights itself—to specific pieces of data within the Content Provider. This allows a very fine-grain permission model to be set up and enforced, but requires careful planning. The grant-uri-permission configurations must be set up in the manifest. It is important to get these configurations right so that permissions cannot be temporarily granted to data that should never be accessed by components that do not carry the permissions specified for the Content Provider. It is strongly recommended that you use individual grant-uri-permission entries instead of the overall grant-uri-permissions attribute for the entire Content Provider, if the data within the provider can be segregated into sections that can be safely shared and those that cannot. Finally, once permissions are temporarily granted, they must be revoked once the necessary actions are carried out, so that permission is granted only during the time period it is needed for.

Broadcasts are a key technique for intercomponent communications. Mechanisms exist so that the senders of broadcasts can require any receivers that want to receive them to have certain permissions, and this should be enforced if the broadcast is at all sensitive. The opposite mechanism, allowing Broadcast Receivers to accept broadcasts only from those senders with specific permissions, allows receivers to accept messages only from those senders that they can trust (because they have been granted the permission). When using broadcasts to communicate information about anything sensitive, both mechanisms should be deployed, authorizing both receivers and senders, and barring unauthorized components from the system altogether.

Individually, each component type allows you to specify whom the component should be allowed to talk to. Put together, these mechanisms allow an Android app composed of multiple pieces to fully control how those pieces interact, both with other components of the same app and those that are part of different apps. Once the architecture of an app has been designed, applying the principles we have just discussed should be a fairly straightforward task and will result in communications that correctly employ the Principle of Least Privilege and a robust, rugged, more secure app.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required