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

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

Описание

Операционная система Android имеет богатое межпроцессное взаимодействие (IPC), построенное на базе Intent. Приложение может получать данные от других приложений или операционной системы через экспортированные компоненты. Экспортированность комопнента в современных версиях Андроид определяется наличием атрибута 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 }
}

Проблема

Eсли приложение аварийно завершается от пришедшего 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

К началу