Хранимый XSS
Хранимый XSS (также известный как постоянный или Stored XSS) — это тип межсайтового скриптинга (XSS), при котором вредоносный код (скрипт) постоянно сохраняется на сервере веб-приложения (например, в базе данных, на файловой системе, в логах) и затем выполняется в браузере пользователя при каждом обращении к уязвимой странице. В отличие от отражённого XSS (Reflected XSS), хранимый XSS не требует отправки специально сформированной ссылки жертве, так как код автоматически загружается и исполняется при просмотре легитимного контента.
Механизм атаки
Атака с использованием хранимого XSS включает несколько этапов:
- Внедрение вредоносного кода. Злоумышленник находит на веб-сайте форму ввода данных, которая не фильтрует или не экранирует пользовательский ввод (например, поле для комментариев, имя пользователя, сообщение на форуме, отзыв о товаре). В это поле он вводит строку, содержащую HTML-теги и JavaScript-код, например:
<script>alert('XSS')</script>. - Сохранение на сервере. Сервер принимает ввод и сохраняет его в базу данных или другое хранилище без надлежащей санитизации (очистки от опасных символов).
- Загрузка страницы жертвой. Когда другой пользователь (или сам злоумышленник) открывает страницу, на которой отображается сохранённый контент (например, страницу с комментариями), сервер извлекает данные из хранилища и вставляет их в HTML-код страницы.
- Исполнение скрипта. Браузер жертвы интерпретирует вставленный код как часть HTML-документа и выполняет вредоносный JavaScript-скрипт в контексте безопасности этого сайта.
Отличия от других типов XSS
Хранимый XSS отличается от других основных типов межсайтового скриптинга:
- Отражённый XSS (Reflected XSS). Вредоносный код не сохраняется на сервере, а передаётся в запросе (например, в URL-параметре) и сразу же отражается в ответе сервера. Для атаки требуется, чтобы жертва перешла по специально сформированной ссылке.
- DOM-based XSS. Уязвимость возникает на стороне клиента, когда JavaScript-код страницы динамически изменяет DOM-дерево, используя данные из ненадёжного источника (например,
document.location.hashилиwindow.name), без сохранения на сервере.
Ключевое отличие хранимого XSS — его постоянство и способность поражать всех пользователей, посещающих уязвимую страницу, без необходимости активных действий с их стороны (например, клика по ссылке).
Примеры уязвимых мест
Хранимый XSS часто встречается в следующих функциональных возможностях веб-приложений:
- Комментарии и форумы. Поля для ввода текста сообщений, имени пользователя, темы.
- Профили пользователей. Поля «О себе», «Имя», «Город», «Сайт».
- Блоги и CMS. Поля для заголовков, содержимого статей, мета-тегов.
- Системы отзывов и рейтингов. Поля для текста отзыва.
- Чат-системы и мессенджеры. Сообщения, которые отображаются в истории чата.
- Логи и системы мониторинга. Если данные логов выводятся на веб-страницу без экранирования.
Потенциальный ущерб
Последствия успешной атаки хранимого XSS могут быть серьёзными:
- Кража сессионных cookie. Скрипт может отправить cookie жертвы на сервер злоумышленника, что позволяет угнать сессию и получить доступ к учётной записи жертвы.
- Перенаправление на фишинговые сайты. Пользователь может быть перенаправлен на поддельную страницу входа, где у него будут украдены логин и пароль.
- Дефейс (изменение внешнего вида) сайта. Скрипт может изменить содержимое страницы, отображая произвольный текст, изображения или ссылки.
- Загрузка вредоносного ПО. Пользователь может быть принуждён к загрузке и запуску вредоносного файла.
- Выполнение произвольных действий от имени пользователя. Скрипт может отправлять запросы от имени жертвы (например, публиковать комментарии, переводить деньги, менять настройки), используя её сессию.
- Распространение червя. Вредоносный скрипт может самовоспроизводиться, создавая новые комментарии или сообщения, содержащие тот же скрипт, что приводит к массовому заражению.
Методы защиты
Защита от хранимого XSS основывается на принципе «никогда не доверяй пользовательскому вводу». Основные методы:
Валидация и санитизация ввода
- Валидация на стороне сервера. Проверка вводимых данных на соответствие ожидаемому формату (например, для поля «Возраст» — только цифры, для поля «Имя» — только буквы и пробелы). Всё, что не соответствует формату, должно отвергаться.
- Санитизация (очистка) ввода. Удаление или экранирование опасных символов и HTML-тегов. Для этого используются специализированные библиотеки, такие как HTML Purifier (PHP) или OWASP Java HTML Sanitizer. Рекомендуется удалять все HTML-теги, кроме безопасного разрешённого набора (например, для форматирования текста в комментариях).
Экранирование вывода
- Контекстно-зависимое экранирование. При вставке данных в HTML-код страницы необходимо экранировать их в зависимости от контекста (HTML-тело, атрибут тега, JavaScript-строка, CSS-значение). Для этого используются шаблонизаторы (например, Twig, Jinja2, React JSX) с автоматическим экранированием.
- Использование Content Security Policy (CSP). HTTP-заголовок
Content-Security-Policyпозволяет ограничить источники, из которых браузер может загружать и выполнять скрипты. Например, директиваscript-src 'self'запретит выполнение любых инлайн-скриптов, включая внедрённые через XSS.
Прочие меры
- HTTPOnly cookie. Установка флага
HttpOnlyдля сессионных cookie запрещает доступ к ним из JavaScript-кода, что предотвращает их кражу через XSS. - Регулярные аудиты безопасности. Использование автоматических сканеров уязвимостей (например, OWASP ZAP, Burp Suite) и ручное тестирование на проникновение.
- Обучение разработчиков. Понимание принципов безопасного кодирования и типов XSS-атак.
Пример уязвимого и защищённого кода
Уязвимый код (PHP)
```php <?php // Сохранение комментария $comment = $_POST['comment']; // Вставка в БД без фильтрации $db->query("INSERT INTO comments (text) VALUES ('$comment')");
// Вывод комментария $result = $db->query("SELECT text FROM comments"); while ($row = $result->fetch_assoc()) { echo "<p>" . $row['text'] . "</p>"; // Опасный вывод без экранирования } ?> ```
Защищённый код (PHP)
```php <?php // Сохранение комментария с санитизацией $comment = htmlspecialchars($_POST['comment'], ENT_QUOTES, 'UTF-8'); // Вставка в БД с использованием подготовленных выражений $stmt = $db->prepare("INSERT INTO comments (text) VALUES (?)"); $stmt->bind_param("s", $comment); $stmt->execute();
// Вывод комментария $result = $db->query("SELECT text FROM comments"); while ($row = $result->fetch_assoc()) { echo "<p>" . htmlspecialchars($row['text'], ENT_QUOTES, 'UTF-8') . "</p>"; // Безопасный вывод } ?> ```
Источники
- OWASP (Open Web Application Security Project). "Cross-Site Scripting (XSS)". OWASP Foundation.
- OWASP. "DOM based XSS Prevention Cheat Sheet". OWASP Foundation.
- OWASP. "XSS Filter Evasion Cheat Sheet". OWASP Foundation.
- PortSwigger Web Security Academy. "Cross-site scripting (XSS)". PortSwigger Ltd.
- Acunetix. "Stored XSS (Persistent XSS)". Acunetix.
- MDN Web Docs. "Content Security Policy (CSP)". Mozilla Corporation.
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →