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

Возможность показа произвольного сообщения через Intent в BroadcastReceiver

Критичность: СРЕДНИЙ
Способ обнаружения: IAST

Описание

Дефект возникает, когда экспортируемый BroadcastReceiver принимает данные из входящего Intent и отображает их пользователю (например, через Toast, уведомление или диалог) без проверки источника и содержимого. Поскольку экспортируемый приёмник может быть вызван любым приложением на устройстве, злоумышленник получает возможность показывать пользователю произвольный текст от имени доверенного приложения.

Пример уязвимого кода:

public class MessageReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // текст полностью контролируется отправителем broadcast
        String message = intent.getStringExtra("message");
        Toast.makeText(context, message, Toast.LENGTH_LONG).show();
    }
}
<receiver android:name=".MessageReceiver" android:exported="true">
    <intent-filter>
        <action android:name="com.example.SHOW_MESSAGE"/>
    </intent-filter>
</receiver>

Любое приложение может отправить:

Intent i = new Intent("com.example.SHOW_MESSAGE");
i.setPackage("com.example.victim");
i.putExtra("message", "Ваш аккаунт заблокирован. Перейдите по ссылке ...");
sendBroadcast(i);

Проблема

  1. Фишинг и социальная инженерия — злоумышленник показывает пользователю поддельные уведомления (о блокировке счёта, необходимости подтвердить операцию и т. п.) от имени доверенного приложения, склоняя к опасным действиям.
  2. Подмена доверия к интерфейсу — пользователь воспринимает сообщение как легитимное, поскольку оно отображается внутри известного приложения.
  3. Возможная инъекция форматирования/разметки — при использовании сообщения в более сложных UI-элементах непроверенный текст может приводить к дополнительным проблемам отображения.

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

  1. Не экспортируйте приёмник без необходимости: укажите android:exported="false", если broadcast предназначен только для внутреннего использования.

  2. Защитите приёмник разрешением (permission с уровнем signature), чтобы его могли вызывать только доверенные компоненты:

    <permission android:name="com.example.permission.INTERNAL"
                android:protectionLevel="signature"/>
    <receiver android:name=".MessageReceiver"
              android:exported="true"
              android:permission="com.example.permission.INTERNAL"/>
    

  3. Для внутренних сообщений используйте observable-механизмы (LiveData / Kotlin Flow) или регистрируйте приёмник с Context.RECEIVER_NOT_EXPORTED (API 33+). LocalBroadcastManager для этого использовать не стоит — он deprecated с 2019 года.

  4. Проверяйте источник и валидируйте содержимое входящего Intent: не отображайте пользователю текст, пришедший из недоверенного источника, без фильтрации.

  5. Не передавайте текст сообщения через extras внешнего Intent — используйте предопределённые на стороне приложения шаблоны/идентификаторы сообщений вместо произвольных строк.

Ссылки

  1. https://developer.android.com/guide/components/broadcasts
  2. https://developer.android.com/privacy-and-security/security-tips#broadcast-receivers
  3. https://mas.owasp.org/MASTG/0x05h-Testing-Platform-Interaction/
  4. https://cwe.mitre.org/data/definitions/502.html
К началу