Часть 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