Часть 5: JavaScript Injection в Flutter WebViews - неавторизованный веб-контент

Часть 5: JavaScript Injection в Flutter WebViews - неавторизованный веб-контент

FlutterPulse

Эта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀

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

Но если вы не обрабатываете их безопасно, WebViews могут стать открытыми шлюзами для внедрения JavaScript, выполнением кода Dart, и кражей учетных данных — особенно при работе с недостоверным или внешним веб-контентом.

В этой статье мы углубляемся в то, как злоумышленники эксплуатируют Flutter WebViews и как правильно защитить их.

🚨 Какова проблема?

При использовании InAppWebView или WebView в Flutter, разработчики часто:

  • Загружают внешние или пользовательские URL
  • Включают不ограниченное выполнение JavaScript
  • Обнажают родные мосты Dart (JavaScriptHandler, evaluateJavascript)
  • Не санизируют или проверяют загружаемый контент

Это открывает дверь для:

  • Внедрения JavaScript (через URL-параметры, недоверительный HTML)
  • Удаленного выполнения кода (через JS → обработчики Dart)
  • Кражи учетных данных и захвата сессии
  • Извлечения данных из WebView для злоумышленника

🕵️‍♂️ Реальный сценарий эксплуатации

Цель: Выполнить вредоносный JavaScript внутри WebView и потенциально вызвать функции Dart или украсть данные.

🔥 Эксплуатация #1: XSS посредством инъекции URL-запроса

Предположим, вы загружаете контент следующим образом:

final url = 'https://example.com/profile?user=${widget.username}';
webViewController.loadUrl(url);

Если username = "<script>alert('hacked')</script>", и бэкэнд отражает его — XSS происходит.

🔥 Эксплуатация #2: Выполнение кода Dart посредством JS-моста

Flutter WebViews часто используют JS-мосты, такие как:

webViewController.addJavaScriptHandler(
 handlerName: 'sendToFlutter',
 callback: (args) {
 doSomethingSecure(args[0]); // <-- Опасно!
 },
);

Атакующие могут теперь внедрить:

window.flutter_inappwebview.callHandler('sendToFlutter', 'malicious_payload');

Если doSomethingSecure() изменяет состояние приложения, выполняет навигацию или утечку данных — это становится критической уязвимостью.

Реальное влияние

Уровень риска: ВЫСОКИЙ

️ Как исправить

Исправление 1: Никогда не доверяйте динамическому веб-контенту

Загружайте контент WebView только из доверенных, контролируемых источников.

if (Uri.parse(url).host == 'example.com') {
  webViewController.loadUrl(url);
} else {
  // Блокировать или санитизировать
}

Исправление 2: Отключить JavaScript, если он не нужен

initialOptions: InAppWebViewGroupOptions(
  crossPlatform: InAppWebViewOptions(
    javaScriptEnabled: false,
  ),
)

Включайте JS только если абсолютно необходимо.

Исправление 3: Проверять ввод и экранировать строки

Если вы должны передать данные из Flutter → WebView, санируйте их сначала:

final safeUsername = Uri.encodeComponent(username);
webViewController.loadUrl('https://example.com?user=$safeUsername');

На бэкенде экранировать HTML и удалить скрипты перед рендерингом динамических значений.

Исправление 4: Ограничить обработчики JavaScript

Используйте именованные обработчики только для хорошо определенных взаимодействий, и всегда валидировать аргументы:

webViewController.addJavaScriptHandler(
  handlerName: 'submitForm',
  callback: (args) {
    if (args.isNotEmpty && args[0] is String && args[0].length < 100) {
      saveToBackend(args[0]);
    } else {
      throw Exception('Недопустимый ввод');
    }
  },
);

Исправление 5: Использование CSP и HTTP-заголовков на размещенных страницах

Если вы контролируете сервер:

  • Используйте заголовки Content-Security-Policy
  • Предотвращайте выполнение инлайн-скриптов и непроверенных скриптов
  • Очищайте и кодируйте весь динамический контент
  • Добавьте X-Frame-Options, Referrer-Policy и другие

Безопасная конфигурация примера WebView

InAppWebView(
  initialUrlRequest: URLRequest(url: Uri.parse("https://вашдомен.com")),
  initialOptions: InAppWebViewGroupOptions(
    crossPlatform: InAppWebViewOptions(
      javaScriptEnabled: true,
      useShouldOverrideUrlLoading: true,
      clearCache: true,
    ),
  ),
  onWebViewCreated: (controller) {
    controller.addJavaScriptHandler(
      handlerName: "safeHandler",
      callback: (args) {
        if (args.isNotEmpty && args[0] == "valid") {
          // Безопасное действие
        }
      },
    );
  },
)

Анти-паттерны, которых следует избегать

Чек-лист для разработчиков

  • Загружайте WebViews только из доверенных URL
  • Отключайте JavaScript, если он не абсолютно необходим
  • Проверяйте все аргументы моста JS→Dart
  • Очищайте пользовательские входные данные и экранируйте URL
  • Используйте CSP и заголовки безопасности для размещенного контента
  • Избегайте раскрытия привилегированной логики Dart для JS

Report Page