Как бороться с XSS атаками во фронтенде?
Что проверяют
Понимаете ли вы, откуда в современном фронтенде берётся XSS, если фреймворк сам экранирует вывод. Следом обычно просят рассказать про CSP или спрашивают, где хранить токен, чтобы XSS его не украл.
Как отвечать
Начните с типов: хранимая (скрипт лежит в базе и отдаётся всем), отражённая (приходит из параметра URL в ответ сервера) и DOM-based, где уязвим сам клиентский код, который берёт данные из location.hash или postMessage и вставляет в страницу.
Защита строится слоями:
- экранирование по умолчанию: React, Vue и Angular экранируют текст в шаблонах. Опасны места, где это выключено:
dangerouslySetInnerHTML,v-html,bypassSecurityTrustHtml; - опасные приёмники:
innerHTML,insertAdjacentHTML,document.write,eval,new Function,setTimeoutсо строкой. Для текста естьtextContent; - санитизация: если HTML нужен (редактор, комментарии с разметкой), его пропускают через DOMPurify прямо перед вставкой;
- URL: адрес из пользовательских данных в
hrefможет начинаться сjavascript:. Разрешайте толькоhttpиhttps; - CSP: заголовок
Content-Security-Policyсscript-srcна nonce или хэшах. Даже если инъекция прошла, браузер не выполнит чужой инлайн-скрипт. Внедрять удобно черезContent-Security-Policy-Report-Only; - Trusted Types:
require-trusted-types-for 'script'запрещает передавать в опасные приёмники обычные строки. Поддержку в целевых браузерах проверьте, это дополнительный слой.
Про последствия: сессионную куку ставят с HttpOnly, тогда скрипт её не прочитает, а токен в localStorage доступен любому внедрённому коду.
Где спотыкаются
Говорят «у нас React, XSS невозможен». Хватает одного dangerouslySetInnerHTML с разметкой из CMS или ссылки с javascript:.
Пишут одну функцию экранирования на все случаи или чистят данные регэкспом на входе. Контексты разные: текст внутри тега, атрибут, URL и строка внутри <script> требуют разного кодирования, и делать это нужно при выводе.
Добавляют CSP с 'unsafe-inline' и 'unsafe-eval' и считают задачу закрытой. Такая политика почти не мешает атаке, её смысл как раз в запрете инлайна.