XSS (Cross-Site Scripting) — тип веб-уязвимости, при котором злоумышленник внедряет вредоносный JavaScript-код в страницу приложения. Этот код выполняется в браузере другого пользователя и получает доступ к данным страницы, cookies, токенам авторизации, содержимому форм и API.
Наиболее распространённые последствия:
Пример опасного ввода:
<script>alert('XSS')</script>
Если приложение выводит такой текст без обработки:
element.innerHTML = userInput;
браузер интерпретирует строку как HTML и выполнит
<script>.
Вредоносный код сохраняется в базе данных и затем отображается другим пользователям.
Пример:
<script>fetch('/steal-cookie')</script>
Сценарий:
Вредоносный код передаётся через URL или параметры запроса и сразу отображается сервером.
Пример URL:
https://site.com/search?q=<script>alert(1)</script>
Если сервер вставляет параметр q в HTML без фильтрации,
код выполняется.
Уязвимость возникает исключительно на стороне клиента.
Опасный пример:
const params = new URLSearchParams(location.search);
document.body.innerHTML = params.get('message');
Параметр URL:
?message=<img src=x oner ror=alert(1)>
Validator.js предназначен для:
Библиотека особенно полезна:
Установка:
npm install validator
Подключение:
const validator = require('validator');
ESM:
import validator from 'validator';
Главный инструмент защиты от XSS в Validator.js — метод
escape().
Он заменяет специальные HTML-символы безопасными HTML-сущностями.
const validator = require('validator');
const input = '<script>alert("XSS")</script>';
const safe = validator.escape(input);
console.log(safe);
Результат:
<script>alert("XSS")</script>
Браузер больше не воспринимает строку как HTML.
escape() преобразует:
| Символ | Результат |
|---|---|
< |
< |
> |
> |
" |
" |
' |
' |
& |
& |
/ |
/ |
Без защиты:
app.post('/comment', (req, res) => {
const comment = req.body.comment;
saveComment(comment);
});
Опасный ввод:
<img src=x oner ror=alert(1)>
Защищённый вариант:
app.post('/comment', (req, res) => {
const comment = validator.escape(req.body.comment);
saveComment(comment);
});
Одна из распространённых ошибок — сохранение необработанного HTML в базу данных.
Неправильно:
db.save({
text: req.body.text
});
Правильно:
db.save({
text: validator.escape(req.body.text)
});
Такой подход:
Пользовательский ввод часто содержит:
Комплексная обработка:
const text = validator.escape(
validator.trim(req.body.text)
);
Ввод:
<script>alert(1)</script>
После обработки:
<script>alert(1)</script>
blacklist() удаляет запрещённые символы.
Синтаксис:
validator.blacklist(str, chars);
const input = '<script>alert(1)</script>';
const cleaned = validator.blacklist(input, '<>');
console.log(cleaned);
Результат:
scriptalert(1)/script
Метод не обеспечивает полноценную защиту от XSS.
Опасный пример:
img src=x oner ror=alert(1)
Даже без < и > могут оставаться
вредоносные конструкции.
Поэтому:
blacklist() используется как дополнительная мера;whitelist() оставляет только разрешённые символы.
Пример:
const username = validator.whitelist(
req.body.username,
'a-zA-Z0-9_'
);
Допустимые символы:
_.Опасный ввод:
admin<script>
После обработки:
adminscript
XSS может использоваться через вредоносные ссылки.
Опасный пример:
jav * ascript:alert(1)
validator.isURL(url);
const link = req.body.link;
res.send(`<a href="${link}">Link</a>`);
Вредоносный ввод:
jav * ascript:alert(document.cookie)
const link = req.body.link;
if (!validator.isURL(link, {
protocols: ['http', 'https'],
require_protocol: true
})) {
throw new Error('Invalid URL');
}
Особенно важно запрещать:
jav * ascript:data:vb * script:Безопасная конфигурация:
validator.isURL(url, {
protocols: ['https'],
require_protocol: true
});
Иногда email используется внутри HTML.
Опасный пример:
"><script>alert(1)</script>
Проверка:
validator.isEmail(email);
Очистка:
validator.normalizeEmail(email);
Дополнительная защита:
validator.escape(email);
Практический подход — создание общего санитайзера.
function sanitizeInput(value) {
return validator.escape(
validator.trim(value)
);
}
Использование:
const username = sanitizeInput(req.body.username);
const comment = sanitizeInput(req.body.comment);
app.use((req, res, next) => {
for (const key in req.body) {
if (typeof req.body[key] === 'string') {
req.body[key] = validator.escape(
validator.trim(req.body[key])
);
}
}
next();
});
Механизм:
XSS опасен не только для HTML-страниц.
Пример API:
{
"message": "<script>alert(1)</script>"
}
Frontend может вставить это значение в DOM:
element.innerHTML = data.message;
Поэтому данные желательно очищать ещё на сервере.
app.post('/api/message', (req, res) => {
const message = validator.escape(req.body.message);
res.json({
message
});
});
Иногда данные экранируются несколько раз.
Пример:
validator.escape('<script>');
Результат:
&lt;script&gt;
На странице появляются HTML-сущности вместо текста.
Правило:
Не рекомендуется:
escape();Многие шаблонизаторы уже имеют встроенное экранирование.
Например:
Однако опасность возникает при ручном выводе HTML.
EJS:
<%- userContent %>
<%- %> отключает экранирование.
Безопасный вариант:
<%= userContent %>
React автоматически экранирует строки:
<div>{userInput}</div>
Но опасность появляется при использовании:
dangerouslySetInnerHTML
<div
dangerouslySetInnerHTML={{
__html: userInput
}}
/>
const safe = validator.escape(userInput);
Vue безопасен при обычном выводе:
<div>{{ text }}</div>
Но директива v-html опасна:
<div v-html="text"></div>
Требуется очистка:
this.text = validator.escape(input);
Angular автоматически экранирует значения.
Опасные конструкции:
[innerHTML]
или:
bypassSecurityTrustHtml()
Перед использованием необходимо очищать ввод.
Validator.js не является полноценным HTML-санитайзером.
Библиотека:
Сложный пример:
<svg onl oad=alert(1)>
Или:
<a href="jav * ascript:alert(1)">
Для полноценной HTML-очистки лучше использовать:
Частая архитектура:
| Инструмент | Назначение |
|---|---|
| Validator.js | Валидация и базовая очистка |
| DOMPurify | Полная HTML-санитизация |
import validator from 'validator';
import DOMPurify from 'dompurify';
const input = validator.trim(req.body.text);
const safe = DOMPurify.sanitize(input);
XSS-защита зависит от места вывода данных.
Разные контексты требуют разных механизмов:
| Контекст | Защита |
|---|---|
| HTML | escape |
| URL | encodeURIComponent |
| JavaScript | JSON.stringify |
| CSS | фильтрация значений |
| HTML-атрибут | экранирование кавычек |
Наиболее распространённая причина XSS:
element.innerHTML = userInput;
Безопаснее:
element.textContent = userInput;
или:
element.innerText = userInput;
Даже при использовании Validator.js рекомендуется включать CSP.
Пример заголовка:
Content-Security-Policy: default-src 'self'
CSP:
Если XSS всё же произошёл, HttpOnly снижает риск кражи cookies.
Пример:
Set-Cookie: session=abc123; HttpOnly; Secure
JavaScript не сможет прочитать cookie:
document.cookie
Неправильно:
validator.escape(input);
только в браузере.
Злоумышленник может отправить запрос напрямую к API.
Проверка должна быть:
Даже сохранённые данные могут быть опасны.
Stored XSS часто возникает именно через БД.
Уязвимость может находиться:
Опасный подход:
input.replace(/<script>/g, '');
XSS имеет огромное количество обходов:
<ScRiPt>
<img oner ror=...>
<svg>
import express from 'express';
import validator from 'validator';
const app = express();
app.use(express.json());
function sanitize(value) {
return validator.escape(
validator.trim(value)
);
}
app.post('/comment', (req, res) => {
const username = sanitize(req.body.username);
const text = sanitize(req.body.text);
if (!validator.isLength(username, {
min: 3,
max: 30
})) {
return res.status(400).json({
error: 'Invalid username'
});
}
res.json({
username,
text
});
});
Надёжная архитектура строится из нескольких уровней:
innerHTML;Validator.js является важным элементом этой системы, но максимальная защита достигается только при комплексном подходе.