Enable javascript in your browser for better experience. Need to know to enable it? Go here.
Blogs Banner

Who do you Think Owns Your Android App - Part Two

Part One of this series talks about how effectively intent filters could help you avoid the Confused Deputy problem when developing an Android App. In the second part, I discuss how to securely implement a Broadcast Receiver when developing an Android App.

According to this post, a Broadcast Receiver is ‘a component that responds to system-wide broadcast announcements. Many broadcasts originate from the system—for example, a broadcast announcing that the screen has turned off, the battery is low, or a picture was captured. Applications can also initiate broadcasts—for example, to let other applications know that some data has been downloaded to the device and is available for them to use.’

Let’s take a look at Broadcast receivers where security threats could arise if we are not careful with the component. I am going to use the example of a ticket booking app which involves interaction with third party payment gateways.

Illustrated below, are the stages of interaction with the app. I have also used the illustration to point to the exact instance or place when data could be compromised because of a malicious app.

The three stages in booking a ticket on the app

Land on the Ticket Booking App

Moving to the Payment Gateway on the Ticket Booking App

Malicious App taking control of your information on the Ticket Booking App

Did you notice something unusual? Look closely, again!

  • When the user clicks on ‘Book Tickets’, she proceeds to the ‘Payment Options’ screen
  • When the user decides on her mode of payment, she is prompted to enter her payment details. This is also when the user is sent to another screen/activity
  • However, the screen/activity belongs to another application. This is a Malicious App! And it is prompting you to enter your credit card information. (hint: keep an eye out for consistency in the app’s visuals, design and user experience)

Usually, it's at the third party app where Data Security Breach could happen. This is a pertinent and avoidable case of data theft. Before I get into how this can be avoided, let’s figure out how the deception is even possible in the first place.

There are risks when we send broadcasts, globally. When you use sendBroadcast(Intent) or related methods, it’s normal for any other application to receive these broadcasts.

This illustration, below will better explain what I am trying to convey.
Global Broadcasts in an Android App are susceptible to data breach
Now, that we know data security is at stake, it becomes quite obvious that we should look at how we can safeguard our android apps from being a springboard of privilege elevation attacks or information leaks.

Here are three approaches

Through permissions
The code can be accessed here

To enforce a permission when ‘sending’ information, you supply a permission in the Android manifest:

<permission 
    android:name=”com.booking.permission.PAYMENT_PERMISSION”
    android:protectionLevel=’’normal”/>
<uses-permission android:name=”com.booking.permission.PAYMENT_PERMISSION”/>

Before

public void makePayment(View view) {
    Intent intent = new Intent();
    intent.setAction(“com.booking.action.PAY”);
    sendBroadcast(intent);
}

After

public void makePayment(View view) {
    Intent intent = new Intent();
    intent.setAction(“com.booking.action.PAY”);
    sendBroadcast(intent, “com.booking.permission.PAYMENT_PERMISSION”);
}

Only receivers who have been granted this permission will be able to receive the broadcast. The receiver should request the permission via

<receiver android:name=”.UserDefinedBroadcastReceiver”>
<android:permission=”com.booking.permission.PAYMENT_PERMISSION”>
    <intent-filter>
        <action android:name=”com.booking.action.PAY”/>
        <category android:name=”android.intent.category.DEFAULT”/>
    </intent-filter>
</receiver>

Intent.setPackage
The code can be accessed here

Starting with the ICE_CREAM_SANDWICH (API 14), you can also restrict the broadcast to your application with package settings. Every application must have a unique package name. To quote from the API guide, ‘The package name serves as a unique identifier for the application’ and ‘Once you publish your application, you cannot change the package name.’

 public void makePayment(View view) {
    Intent intent = new Intent();
    intent.setAction(“com.booking.action.PAY”);
    intent.setPackage(“com.booking”); 
    sendBroadcast(intent);
}

‘setPackage’ specifies the application package of the component that can handle the intent. The package needs to match your apps package or you can't receive the broadcast. As the “Application Id” is unique, the malicious app is bound to fail, here.

Use LocalBroadcastManager (Available in the v4 support library)
The code can be accessed here

LocalBroadcastManager localBroadcastManager =LocalBroadcastManager.getInstance(this); 
localBroadcastManager.sendBroadcast(intent);

You need to register for the local broadcast message of interest.
Advantages of using LocalBroadcastManager over sending global broadcasts with sendBroadcast(Intent) is that the broadcasted data will not be intercepted by malicious apps.

LocalBroadcastManager.getInstance(this).registerReceiver(userDefinedBroadcastReceiver, new IntentFilter(PAYMENT_ACTION)); 

And un-register after the operation is done

LocalBroadcastManager.getInstance(this).unregisterReceiver(userDefinedBroadcastReceiver);

The above approaches help safeguard any other app from receiving your broadcasts.

Now that we are aware of three approaches to safeguard our app, let’s quickly clear up where and when these approaches can be applied.

As for the code resources, the example of the Ticket Booking App’s code alongside its revisions can be accessed here. The Malicious Broadcast Receiver code can be accessed here

The above approaches help safeguard any other app from receiving your broadcasts. 

In a similar manner you also need to safeguard your receivers from receiving any unintended broadcasts. A check on the action in the onReceive should suffice. If a receiver is registered via an intent filter, you might want to use exported="false" in the manifest definition to prevent the broadcast receiver from receiving intents from other applications.

That covers broadcast receivers and what can be done to avoid data security risks when implementing the former.

In the third part, we will look at why requested permissions should be minimal because otherwise, they can lead to risks of data loss or theft. We will also clear a couple of pertinent points with regards to the confusing ‘logging’, in Android.

Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Thoughtworks.

Keep up to date with our latest insights