Securing Content Providers
The data stored in a Content Provider is accessed via content URIs.
A content URI is a URI of the form
authority refers to the Content Provider itself, and
path/id to data stored within the Content Provider.
To access data stored within a Content Provider an app must use the
delete() methods provided by a
ContentResolver object. These methods provide synchronous RPC like access to the Content Provider.
Access to the Content Provider through these methods can be controlled using permissions, however as we shall see, Content Providers allow much finer access control than Activities and Services.
The different mechanisms for applying permissions that we cover below have different scopes, from those that apply to the whole of the Content Provider, through to those that only apply to individual data items.
If two permissions apply to the same content URI, then the one with the smaller scope, i.e. the more fine-grained permission, is the one that takes precedence.
Basic access control
The most basic level of access control is performed in much the same way as for Activities and Services by using the
android:permission attribute of the
<provider> element in the app’s
This defines a permission that controls both read and write access to the whole of the Content provider.
However, following the Principle of Least Privilege, we can apply separate read and write permissions.
Sometimes we would like other apps to be able to read the contents of a Content Provider but not change them, and sometimes we want to allow an app to add a new entry to our database, but not to be able to read what is already in there. For example a logging system where apps can record what they are doing, but cannot read what other apps are doing.
This means that having write permission does not imply having read permission. This is a common misconception.
Read permission is controlled by the
android:readPermission attribute of the
<provider> element, and write permission by the
The access methods of a Content Provider are controlled as follows:
query()requires read permission, and
delete()require write permission.
Controlling access to subsets of the data
To further apply the Principle of Least Privilege we can give separate permissions to subsets of a Content Provider’s data. This will override any global permissions set on the whole of the Content Provider.
We can do this by adding
<path-permission> elements to the
There are three different attributes that can be set to control which subset of the data the permissions apply to:
applies the permissions to entries within
/subpath, but not to subdirectories of
applies the permissions to entries within subdirectories of
allows the use of wildcards to match the content URIs for which the permissions apply.
For example if we wanted to separately control access to the data stored under the content URIs
content://authority/subpath2 we would add the following:
<provider …> … <path-permission android:pathPrefix="/subpath1" android:readPermission="com.example.myamazingapp.SUBPATH1_READ_PERMISSION" android:writePermission="com.example.myamazingapp.SUBPATH1_WRITE_PERMISSION" /> <path-permission android:pathPrefix="/subpath2" android:readPermission="com.example.myamazingapp.SUBPATH2_READ_PERMISSION" android:writePermission="com.example.myamazingapp.SUBPATH2_WRITE_PERMISSION" /> </provider>
By applying different permissions to the two content URIs we can give an app access to
content://authority/subpath1 but deny it access to
Temporary access to specific data items
We can take the Principle of Least Privilege one step further and consider the case where we might want to give an app temporary access to some of the data within a Content Provider.
One obvious example of this is the case of an email client that may store emails and attachments in a database accessed via a Content Provider.
Now suppose one of the attachments is a media file that the email client does not know how to handle, but for which there is another app on the device that can handle such files. We would like to give that app temporary access to the media file within the Content Provider in order to play the file, but we do not want to allow the app permanent access to the file, nor do we want to give it complete access to all the user’s emails. To handle this situation Android provides URI permissions.
URI permissions allow any component that has permission to access a content URI within a Content Provider to temporarily grant another component that permission, even if that component does not itself have permission to access the Content Provider.
Enabling URI permissions
By default the granting of temporary URI permissions is disabled. It can be turned on in two different ways.
The first way enables any component that has permission to access the Content Provider the ability to grant temporary access to any content URI within the Content Provider to another component. To do this we set the attribute
android:grantUriPermission="true" in the
Again following the Principle of Least Privilege we might not want to do this, so the second way is to add
<grant-uri-permission> elements to the
<provider> element. Like with
<path-permission> elements (above), you can set one of three different attributes:
android:pathPattern. For example,
<provider …> … <grant-uri-permission android:path="/subpath" /> </provider>
allows a component to grant temporary permissions for entries within
/subpath, but not for subdirectories of
Granting temporary permissions
So far we have discussed how we can give a component permission to grant temporary permissions to other components, however we have not yet said anything about how to actually grant temporary permissions. This is done programatically, and there are two ways to do this.
The first, and preferred way, is through the setting of flags on Intents. The way this works is as follows:
A component wants to ask another component, like an Activity (say an image viewer) or a Service, to perform an action on some data in a Content Provider for which it has access.
As usual, the component will create an Intent and call
startService()with that Intent.
To tell the receiving component what data it should perform the action on the sending component calls the
setData()method with the required content URI.
In order to give the component that receives the Intent permission to access the content URI where the data is stored, the sending component uses the
setFlags()method to set either the
FLAG_GRANT_WRITE_URI_PERMISSIONflag on the Intent before sending it.
startService()etc.) is called, the system will check whether the calling component has permission to grant the temporary access to the content URI, if it does, the Intent will be delivered to the receiving component.
The second way to grant temporary permission to access a content URI to another app is by calling the
grantUriPermission() method, but this is not recommended.
revokeUriPermission() method can be used to revoke temporary permissions, and must be called if a content URI for which a temporary permission is granted, is removed from the Content Provider.
© University of Southampton 2017