Перейти к содержанию

Ошибка в приложении при работе через IPC

Описание

Операционная система Android имеет богатое межпроцессное взаимодействие (IPC), построенное на базе Intent. Приложение может получать данные от других приложений или операционной системы через экспортированные компоненты. Экспортированность компонента в современных версиях Android определяется наличием атрибута exported="true" в теге объявленного компонента в файле AndroidManifest.xml.

<manifest 
   <application
        <activity
             android:name=".DashboardActivity"
             android:exported="true">
        </activity>
    </application>
</manifest>

Если компонент экспортирован, он может принимать Intent'ы извне (от других приложений). Помимо запуска такого компонента сторонним приложением, в объекте Intent могу передаваться дополнительные данные в виде пар ключ-значение и/или URI. В качестве значений могут передаваться примитивы, строки, массивы, а также целые объекты в Parcelable. Переданные данные извлекаются на стороне целевого приложения и зачастую не валидируются и не санитизируются на наличие некорректных или вредоносных значений. В результате возможны как непосредственные атаки на пользователя/приложение, так и просто ошибки в работе приложения вплоть до его аварийного завершения.

Пример создания Intent для последующего запуска DashboardActivity:

class DashboardActivity :
    AppCompatActivity(R.layout.activity_dashboard) {

    companion object {
        private const val KEY_INFO_1 = "KEY_INFO_1"
        private const val KEY_INFO_2 = "KEY_INFO_2"

        fun newIntent(
            context: Context,
            firstInfo: FirstInfo,
            secondInfo: SecondInfo,
        ) = Intent(context, DashboardActivity::class.java)
            .putExtra(KEY_INFO_1, firstInfo)
            .putExtra(KEY_INFO_2, secondInfo)    
    }

startActivity(
   DashboardActivity.newIntent(
      context = context,
      firstInfo = firstInfo,
      secondInfo = secondInfo,
   )
)

Пример получения данных из Intent:

private val firstInfo by requireExtra<FirstInfo>(KEY_INFO_1)
private val secondInfo by requireExtra<SecondInfo>(KEY_INFO_2)

inline fun <reified T> AppCompatActivity.requireExtra(key: String): Lazy<T> {
    return lazy { intent?.extras?.get(key) as T }
}

Проблема

В случае, если приложение аварийно завершается от пришедшего Intent'а, сторонее приложение может периодически посылать такие Intent'ы, сделав невозможным использование целевого приложения. Кроме того, ошибка в приложении при парсинге или прямом использовании данных из Intent'а говорит о наличии потенциальной уязвимости в нем. Злоумышленник может подобрать такой формат данных, чтобы обойти существующие проверки (если они вобще есть), и тогда просто ошибка в приложении превратится в серьезную эксплуатируемую уязвимость, позволяющую совершить атаку на данные пользователя/приложения.

Рекомендации

Следует рассматривать любые приходящие извне данные как вредоносные и соответствующим образом их обрабатывать. Обработку данных, если это возможно, лучше строить на «белых списках» в остальных случаях предусмотреть проверку семантики, синтаксиса и авторизации при использовании пришедших из внешних источников данных.

Пример обработки входящих данных из Intent:

private val deeplinkEvent by requireExtra<DeeplinkEvent?>(KEY_DEEPLINK_EVENT)

if (deeplinkEvent != null && deeplinkEvent is DeeplinkEvent.OpenMyScreenEvent) {
    showMyScreen()
}

Ссылки

  1. Экспортируемость компонентов

  2. Статьи о IPC

  3. O Intent

К началу