PSA: Validate Your ACTION_SEND Inputs
Dominik Schürmann and Lars Wolf, in an excellent
blog post and
pre-pub paper,
points out a security flaw in many activities that have an ACTION_SEND
<intent-filter>
.
Many ACTION_SEND
implementations accept EXTRA_STREAM
as input.
That is supposed to point to Uri
representing a stream of stuff to be
sent somewhere. Email apps might send it as an attachment, for example.
So, you fire up a ContentResolver
, call openInputStream()
to get
at the content backed by that Uri
(because
that’s how real developers do it),
and then do something with that content. You might not even really care
what the content is… until that content is something from your own app. Like, say, your
user account database.
The problem outlined in Mr. Schürmann’s post and paper is that a malicious
party could provide you with a file:
Uri
to your own internal storage.
While the third-party app cannot access your internal storage, you can.
So, in the case of an email app, the attacker asks you to email one of
your app’s own files to the attacker’s email address. The user may be
involved in this (e.g., having to actually click something to send
the email), but with
a bit of phishing or social engineering,
that problem can
be handled, at least some of the time. After all, courtesy of the intent:
scheme, some Web browsers and the like will allow a simple link click to trigger
the evil ACTION_SEND
request.
To help with this, cketti (of K-9 Mail fame) wrote
a SafeContentResolver
that has its own openInputStream()
method. However, this one will
fail if the Uri
points to a file that your app owns. If you use this
instead of the openInputStream()
on ContentResolver
, your ACTION_SEND
implementation will be safer from this attack.
You might also think that this problem will fade away in the coming years. After
all, the Grim Reaper has a close eye on the file:
scheme,
and so in the fullness of time, file:
Uri
values will be few and
far between.
Alas, the same problem can occur if the attacker sends you a content:
Uri
to your own ContentProvider
, such as a FileProvider
. Even though
the provider may not be exported, your app has full access to its
own providers, just as your app has full access to its own internal storage.
I am working on updates to my StreamProvider
,
and I’ll try to add some stuff in that will make this attack more difficult
to pull off.
More generally, if you accept input from outside parties, validate it.
Have rules for what sorts of Uri
values you will and will not accept
for things like EXTRA_STREAM
,
and provide runtime checks to confirm that the values you receive follow
the rules.