How dangerous permissions sneak into apps

02 Aug 2018

How dangerous permissions sneak into apps

2 minute read

This is a post-mortem where the very dangerous permission, READ_PHONE_STATE, unintentionally sneaked into our app. Here’s how this could happen, how we debugged and finally how we solved it.

Prologue

Sprint comes to an end and we’re happy to deliver a new release of our app. After rolling it out to our beta community without issues, we move ahead to production.

While everything looks fine at first, after a while we see users complaining:

Users complaining: Why does this app want to know if I'm in a phone call and who I'm calling?

Honestly we were completely taken aback by this… But sure enough looking at the play store:

Google play store permissions

Root cause

If you ever run into a similar issue, the Android Studio merged manifest view is the way to go. Just open your manifest and click the Merged manifest tab at the bottom.

Google play store permissions

Sure enough, the READ_PHONE_STATE permission is there.

Unfortunately, this view couldn’t help us find where the permission was merged from:

  • Double-clicking the permission led us back to normal manifest view
  • Color coding palette is so subtle that we couldn’t see what color the permission was highlighted in

Color highlighting of manifest merger needs an extremely trained eye to map it on the legend

Fortunately, the manifest merger also prints a log file to build/outputs/logs that describes where everything is merged from.

Output logs of the manifest merger are located at build/outputs/logs

This file gave a clear answer:

uses-permission#android.permission.READ_PHONE_STATE
IMPLIED from /app/src/debug/AndroidManifest.xml:8:1-15:12
reason: hue.libraries.translations has a targetSdkVersion < 4

Wow… That’s nasty!

A while ago we decided to move all our translations to a new module, with an empty manifest and a bare-bones build.gradle file:

apply plugin: 'com.android.library'

android {
    compileSdkVersion Config.compileSdk
}

And because we didn’t explicitly set the targetSdk, a targetSdk of 1 is assumed and hence we end up with a dangerous permission!

To be fair, the documentation does warn you about this:

Google play store permissions

But still… wow!

Solution

While a solution could be to simply set the targetSdk in our translations module. This wouldn’t prevent something similar from happening in the future.

Therefore we decided to set the targetSdk (and others) for all our modules in the top level build.gradle file. This also keeps submodule build.gradle files lean.

subprojects {
    afterEvaluate { project ->
        if (project.plugins.findPlugin('android') ?: project.plugins.findPlugin('android-library')) {
            android {
                buildToolsVersion Config.buildTools
                compileSdkVersion Config.compileSdk

                defaultConfig {
                    minSdkVersion Config.minSdk
                    targetSdkVersion Config.compileSdk
                }

                compileOptions {
                    sourceCompatibility Config.javaVersion
                    targetCompatibility Config.javaVersion
                }
            }
        }
    }
}

Additionally, we also wanted to protect ourselves against 3rd party library developers that could make this mistake. To do so, you can inform the manifest merger to remove the permission while merging by adding the following to your manifest:

<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove"/>

That is overkill you say?

Well, Firebase AND Google play services already made this booboo in the past.

… wow!

Wrap-up

Not explicitly setting your target SDK version will cause a dangerous permission to sneak into your app. Make sure you set the target SDK in every module and protect yourself from 3rd party libraries.

If you’ve made it this far you should probably follow me on Mastodon. Feel free leave a comment below!

Leave a Comment

Start a conversation about this content on Reddit or Hacker News.