Перейти к содержимому
Все статьи
Безопасность

XSS через чат поддержки: как атакующий пытался украсть сессию администратора

17 октября 2026 г.· 7 мин чтения
Схема: сообщение с HTML-вставкой уходит из чата в API и в админ-панель; три слоя защиты не дают скрипту выполниться и получить данные сессии.
Как была устроена попытка и где её останавливает каждый слой защиты. Открыть схему крупнее

Кейс из практики. Проект — финансовый онлайн-сервис с личными кабинетами, приёмом платежей, чатом поддержки и админ-панелью. Мы ведём его безопасность и инфраструктуру. Название проекта, адреса и даты не раскрываем; технические детали обобщены настолько, чтобы их нельзя было использовать для атаки.

Утром в чат поддержки начали приходить сообщения, похожие на вопросы клиента, но с HTML-вставкой. Расчёт был не на сайт, а на человека: если браузер оператора выполнит вставку в админке, данные его сессии уйдут на внешний сервер.

Что произошло

С одного адреса за утро открылось несколько сессий чата, в каждой — одинаковые сообщения со вставкой. Параллельно тот же адрес регистрировал аккаунты, запрашивал сброс пароля и пробовал войти в админку. Это типичная картина разведки: атакующий одновременно проверяет несколько входов и смотрит, какой откроется.

Чем опасен отложенный XSS

Обычный XSS срабатывает сразу на странице, где его оставили. Отложенный (blind XSS) сохраняется в базе и срабатывает позже в другом интерфейсе — админке, CRM, панели модерации, которые атакующий сам не видит.

Где чаще всего срабатывает отложенная вставка
Канал вводаГде выводитсяКто под угрозой
Чат поддержкиПанель оператораСотрудник поддержки
Форма обращения, тикетАдминка, письмо-уведомлениеАдминистратор
Имя, адрес доставкиСписок клиентов, экспортМенеджер
Заголовки запросаПросмотр логов в браузереРазработчик

Если скрипт выполнится в админке, атакующий может получить сессию администратора или выполнять действия от его имени прямо из его браузера.

Почему вставка не сработала

  • Интерфейсы выводят текст, а не HTML. Сообщения рендерятся через React как строка; вставка пользовательского текста как HTML нигде не используется. Оператор увидел теги буквально, как символы.
  • Cookie админки помечены httpOnly. Даже при выполнении скрипта он не смог бы прочитать токен сессии.
  • Уведомления экранируются. Текст, который уходит сотрудникам в мессенджер, проходит экранирование спецсимволов, поэтому разметка не интерпретируется и там.

Что мы добавили сверху

  1. Отказ на входе. API чата отвечает 400 на сообщения с HTML-тегами, атрибутами-обработчиками и опасными схемами ссылок. Обычным клиентам писать разметку в поддержку незачем.
  2. Блокировка сессий. Сессии чата атакующего помечены как заблокированные и закрыты; API отвечает 403, если сессия заблокирована или открыта с адреса из чёрного списка. Переписку сохранили как доказательство.
  3. Чёрный список в двух местах. Адреса внесены и в правила веб-сервера, и в список, который проверяет само приложение.
  4. Лимиты частоты. На создание сессии и отправку сообщения действует отдельный лимит, чтобы чат нельзя было использовать для массовой рассылки.

Проверьте свой чат и формы

  • Составьте список всех мест, где выводится пользовательский текст: админка, CRM, письма, уведомления, экспорт.
  • Поищите в коде вывод HTML без экранирования (innerHTML, dangerouslySetInnerHTML, v-html, |safe) и убедитесь, что туда не попадают данные клиентов.
  • Проверьте флаги cookie админки: HttpOnly, Secure, SameSite.
  • Включите Content Security Policy хотя бы для админ-панели: запрет внешних адресов в connect-src и img-src обрезает канал утечки.
  • Отправьте в свой чат <b>тест</b>: если оператор видит жирный текст, нужен срочный аудит.

Материалы для проверки

Если нужна реализация

Состав работ зависит от текущего проекта и подтверждённой причины. Можно обсудить диагностику или разработку подходящего решения.

Пентест веб-приложения по OWASP WSTG Сопровождение информационной безопасности Генератор Content Security Policy

Связанный материал: Перебор пароля админки с 200 адресов: почему лимит по IP не спас и что помогло

Вопросы и ответы

Чем blind XSS отличается от обычного XSS?

Обычный XSS срабатывает сразу на странице, где его вставили. Blind XSS сохраняется в базе и выполняется позже в другом интерфейсе — чаще всего в админке или CRM.

Достаточно ли React, чтобы защититься от XSS?

React экранирует строки по умолчанию, но защита пропадает при явной вставке HTML, ссылках с опасными схемами и в сторонних виджетах. Нужны ещё httpOnly-cookie и CSP.

Зачем отклонять HTML на входе, если вывод и так безопасен?

Это второй рубеж: запись не попадает в базу, и будущая ошибка в выводе не превратится в уязвимость. Отказы на входе к тому же видны в логах как попытки атаки.

Нужна помощь с проектом?

Обсудим задачу и предложим решение — от сайта до SaaS и защиты.

Связаться