The Limits of ContentProvider Security
ContentProvider
has a fairly robust integration with the Android
permissions system. You can make a ContentProvider
publicly available,
yet limit access via android:readPermission
and android:writePermission
manifest attributes. Or, you can make a ContentProvider
private, yet
“poke pinholes in the firewall” via android:grantUriPermissions
to
allow third-party access to your content in limited cases. An example
would be an email client making email attachments available to third-party
file viewers (e.g., Adobe Reader for PDFs), but only on user demand,
rather than making all attachments available at all times.
The dirty little secret with all of this is that once your data is in the hands of another app, that app can do whatever it wants with the data, including making that data available to yet other apps. Permissions are not transitive: if A gives data to B (where B has specific permission to get this data), and B gives that data to C, C has no direct relationship with A and does not necessarily need any permissions that A requires of B. From Android’s standpoint, the fact that B happens to hand the same data over to C that it got from A is immaterial.
Now, your reaction to this description may be that B is some form of malware. However, that is not necessarily the case.
For example, suppose B is an email client. Reasonable attention is paid
to the scenario where B receives emails with attachments. At the same
time, though, B probably allows users to send emails with attachments.
Those attachments are not going to be written in B itself, but rather
will represent photos, documents, or other files that exist somewhere
on the Android device. B can use ACTION_GET_CONTENT
or the Storage
Access Framework (ACTION_OPEN_DOCUMENT
) to allow the user to pick
something to attach. However, not only does B have to have the right
to send that attachment to the mail server, but B also needs to be
able to show that attachment to the user via B’s “Sent Items” or equivalent
roster of past sent emails. If the user attached a photo to an email,
B probably should have the feature whereby the sender can view that photo.
But, we have two problems:
-
At the time the email is being crafted and the user picks the photo, the
Uri
that B gets may be good only for B. B may not be able to pass thatUri
along to an image viewer. And while it could be argued that B could implement an image viewer itself, there are an arbitrary number of possible MIME types for attachments, and B cannot implement all of them, realistically. -
What happens if the user deletes, moves, or otherwise modifies the photo, such that the original
Uri
is no longer valid? Now, even B cannot get to the image again, let alone pass that image off to a third-party app.
To address both #1 and #2, B could make a local copy of the photo. However, not only does this represent duplication of data, but now any protections that A had for who could access that photo are lost with respect to B’s copy, and it is now B’s protections that matter.
The security around a ContentProvider
is securing an API. It is not
securing the data. The same holds true for any application of
the Android permission system, such as for securing a service.
The fact that clients of your ContentProvider
can make local copies
of data and share that with others does not mean that you should abandon
the Android permission system, or that you should abandon ContentProvider
.
However, you do need to think through what data you are exposing through
your app’s APIs (including a ContentProvider
) and whether the benefits
for the user of making that data available are worth the risk of the
data being distributed more widely than your permissions might imply.