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.