WebView удобен для встраивания веб-контента в мобильное приложение, но в защите он требует жесткой дисциплины. Я воспринимаю его как отдельную зону риска внутри клиента. Ошибка в настройке открывает путь к подмене страниц, перехвату данных, запуску вредоносного кода через встроенные сценарии и обходу экранов авторизации. Основная проблема не в самом компоненте, а в доверии к внешнему содержимому и в избыточных разрешениях.

WebView

Сценарий атаки выглядит так. Приложение загружает удаленную страницу в WebView. Страница получает доступ к функциям, которые разработчик открыл через мост JavaScript. Если валидация домена слабая, злоумышленник подсовывает другой адрес через редирект, deeplink, пуш-уведомления или подмену ответа в небезопасной сети. После загрузки страница вызывает методы моста, читает токены из локального хранилища, инициирует переходы на фишинговые формы или отправляет команды в нативную часть. Отдельный риск несет смешанный контент, когда защищенная страница подтягивает ресурсы по HTTP.

Признаки атаки

Я начинаю с наблюдаемых симптомов. В приложении внезапно появляются экраны, которых нет в сборке. Окно просит повторный вход без штатного перехода. После нажатия на внутреннюю кнопку открывается форма банка, входа в почту или страница оплаты с чужим адресом. Иногда WebView показывает контент поверх родного интерфейса и блокирует возврат. Тревожный сигнал — неожиданные загрузки после открытия уведомления или перехода по внешней ссылке.

На уровне кода я проверяю три зоны. Первая — источники URL. Если адрес собирается из параметров, полученных извне, риск сразу растет. Вторая — настройки WebView. Опасны включенный JavaScript без жесткой необходимости, доступ к локальным файлам, универсальный доступ из file URL, разрешение на смешанный контент, слабая обработка SSL-ошибок и автоматическое следование непроверенным редиректам. Третья — мост между веб-слоем и нативной частью. Чем шире набор методов, тем выше цена ошибки.

В Android я смотрю на addJavascriptInterface, WebViewClient, shouldOverrideUrlLoading, onReceivedSslError, setAllowFileAccess, setAllowContentAccess, setMixedContentMode и обработку intent-ссылок. Если код продолжает загрузку после ошибки сертификата, я считаю узел уязвимым. Если приложение принимает URL из Intents и сразу передает его в loadUrl, без белого списка доменов, атака получает удобную точку входа. На старых версиях платформы мост JavaScript особенно опасен, если интерфейс открыт для произвольной страницы.

В iOS зона внимания похожа. Я проверяю WKWebView, делегаты навигации, схему открытия ссылок, обработку window.open, передачу сообщений через WKScriptMessageHandler и хранение cookies. Если web-контент получает доступ к нативным командам без проверки origin, злоумышленнику остается лишь добиться загрузки своей страницы. Отдельно оцениваю App-Bound Domains — ограничение доменов для web-контента. При его отсутствии контроль слабее.

Где искать проблему

Я разделяю проверку на статическую и динамическую. Статически читаю код и конфигурацию. Ищу места, где включен JavaScript, где URL приходит из deep link, push payload, буфера обмена, QR-кода, удаленной конфигурации или ответа сервера. Сравнивают фактический список доментов с тем, что зашит в клиенте. Если в проекте есть wildcard-маски или проверки по подстроке, защита слабая. Строка вида endsWith без учета границы домена пропускает поддомен злоумышленника.

Динамически я запускаю приложение через прокси для анализа трафика и наблюдаю, какие страницы уходят в WebView, какие редиректы происходят, какие cookies и заголовки участвуют в запросе. Если приложение доверяет пользовательским сертификатам, перехват трафика покажет слабые места сразу. Если доверие не выдано, я все равно проверяю сетевую политику, чтобы понять, какие соединения разрешены и где возможен обход pinning. Полезна проверка DOM после загрузки страницы: инъекции, внешние скрипты, неожиданные iframe и обращения к нативному мосту видны быстро.

Дальше я моделирую атаки. Передаю в приложение поддельный deep link с внешним URL, меняю redirect на промежуточный домен, подставляю страницу с вызовами мостов, пробую открыть локальный файл, проверяю, блокируется ли переход на нестандартные схемы. Если WebView загружает контент после onReceivedSslError с командой continue, узел закрываю в первую очередь. Если приложение хранит токены в JavaScript-доступном контексте, риск утечки высокий.

Как отключить атаку

Первая мера — сократить область применения WebView. Если экран можно собрать нативно, я убираю веб-слой. Для экранов справки, правил или каталога оставляю только заранее известные адреса по HTTPS. Список доменов задаю явно, без масок. Сравнение выполняю по нормализованному host, схеме и порту. Редиректы на внешний домен блокируют. Внешние ссылки отправляю в браузер, а не внутрь WebView.

Вторая мера — урезать настройки. JavaScript включаю только для тех экранов, где без него нет функции. Доступ к файлам и content URI отключаю, если нет подтвержденной задачи. Смешанный контент запрещаю. Обработчик SSL-ошибок не продолжает загрузку. Автозапуск всплывающих окон отключаю. Кэш, cookies и локальное хранилище оцениваю по сценарию использования, а не по привычке. Для чувствительных потоков, связанных с входом и оплатой, стараюсь не держать сессию внутри общего WebView.

Третья мера — пересобрать мост JavaScript. Я оставляю минимальный набор методов, без доступа к токенам, файловой системе, контактам, геоданным и внутренним командам приложения. Каждую команду привязываю к доверенному origin и к конкретному экрану. Если страница загружена не с разрешенного домена, мост не активируется. В Android полезно убрать addJavascriptInterface там, где хватает evaluateJavascript для односторонней передачи данных. В iOS сообщения от страницы проверяю по имени канала, текущему URL и состоянию навигации.

Еще одна мера — разделение контекста. Для авторизации лучше использовать системный браузерный механизм, а не встроенный WebView. Тогда cookies и защита сеанса обрабатываются системным уровнем, а риск подмены интерфейса ниже. Если внутренний WebView все же остается, я не передаю в него долговременные токены и не рисую критические формы поверх удаленной страницы.

Для контроля после исправлений я добавляю журналирование навигации, отказов по доменам, попыток открыть нестандартные схемы и вызовов мостов. Логи без чувствительных данных дают картину реальных переходов и показывают, откуда пришел опасный URL. На стороне сборки полезны линтеры и правила ревю: запрет continue в SSL-обработчике, запрет универсального file-доступа, запрет загрузки URL из внешних источников без фильтра. После такой чистки WebView остается рабочим инструментом, а не скрытым входом для чужого кода.