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

Уязвимая навигация в Android Jetpack navigation

Описание

В Android для навигации между фрагментами можно использовать NavController, которому передается ресурс с графом навигации (/res/navigation/some_file.xml). Проблема в том, что если в теге <navigation> используется атрибут android:id, то к указанным фрагментам возможен доступ через диплинки. Таким образом стороннее приложение может потенциально открыть фрагмент приложения, указанный в файле (-ах) навигации. Пример атаки хорошо описан в этой статье.

Проблема

В Android можно указать навигацию между экранами, реализованную с помощью фрагментов следующим образом:

  1. Создается файл(ы) с навигацией.

  2. Создаются классы фрагментов.

  3. Устанавливается «корневой фрагмент» — NavHostFragmet, объект NavController которого используется далее для управления навигацией.

Файл навигации может выглядеть следующим образом:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">

    <fragment
        android:id="@+id/navigation_home"
        android:name="ru.ptsecurity.navigation_example.ui.home.HomeFragment"
        android:label="@string/title_home"
        tools:layout="@layout/fragment_home">
        <action android:id="@+id/toFirst"
            app:destination="@+id/navigation_first">
        </action>
    </fragment>

    <fragment
        android:id="@+id/navigation_private"
        android:name="ru.ptsecurity.navigation_example.ui.private.PrivateFragment"
        android:label="@string/title_private"
        tools:layout="@layout/fragment_private" />

    <fragment
        android:id="@+id/navigation_first"
        android:name="ru.ptsecurity.navigation_example.ui.stack.FirstFragment"
        android:label="@string/title_stack"
        tools:layout="@layout/fragment_first">
        <action android:id="@+id/toSecond"
            app:destination="@+id/navigation_second">
        </action>
        <argument
            android:name="textFirst"
            app:argType="string"
            android:defaultValue=""/>
    </fragment>
// ....

Следует понимать, что все атрибуты id имееют числовое значение в файле R.java.

Помимо создания флоу переходов между экранами, после имплементации такой навигации неочевидным образом появляется возможность получить доступ к нужному экрану (фрагменту) с помощью диплинков (см. документацию). По сути, чтобы запустить нужный экран целевого приложения извне, необходимо знать два числовых параметра: android:id нужного тега <navigation> и android:id конкретного фрагмента, который предполагается запустить.

Например, если navigation android:id = 0xA, а fragment android:id = 0xB, то стороннее приложение может сформировать и послать такой Intent:

am start -n com.example.app/.HostActivityClassName --eia "android-support-nav:controller:deepLinkIds" 0xA,0xB

При реализации такой атаки следует учитывать, что разные сборки приложения имеют разные значения для одних и тех же id.

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

Одним из способов защиты является неиспользование атрибута android:id в теге navigation - это не повлияет на навигацию, но диплинки работать перестанут. Однако, в некоторых случаях такой способ не подойдет. Кроме того, лучше всегда явно проверять авторизацию пользователя при доступе к очередному экрану. То есть, проверка возможности доступа должна осуществляться не в навигации, а в бизнес-логике приложения. В случае если пользователь не авторизован (не вводил пин-код или время сессии истекло), то в переходе на запрашиваемый экран ему должно быть отказано.

Ссылки

  1. Android Jetpack navigation

  2. Jetpack navigation deeplink exploitation

  3. Intent'ы

К началу