Безопасность JavaScript: Защита от SQL-инъекций и XSS — Полный гайд для разработчика
Вы когда-нибудь задумывались, почему хакеры так любят веб-приложения? Ответ прост: разработчики часто оставляют «двери» открытыми. Две самые популярные лазейки — это SQL-инъекции и XSS (межсайтовый скриптинг). Даже если вы пишете на JavaScript, это не значит, что вы защищены от атак на серверную часть. В этой статье мы разберем, как распознать уязвимости и, главное, как их закрыть, используя современные практики безопасности.
Почему JavaScript-разработчик должен думать о безопасности?
Многие новички думают: «Я пишу на фронтенде, базы данных не касаюсь — мне ничего не грозит». Это опасное заблуждение. JavaScript сегодня работает везде: на клиенте (браузер), на сервере (Node.js) и даже в базах данных (MongoDB). А значит, ответственность за безопасность ложится на вас. SQL-инъекции и XSS — это не просто скучные термины из учебников, это реальные угрозы, которые могут уничтожить ваш проект, украв данные пользователей или превратив сайт в рассадник вирусов.
SQL-инъекции: Невидимая угроза для вашей базы данных
SQL-инъекция — это атака, при которой злоумышленник «внедряет» вредоносный SQL-код через поля ввода (формы, URL, заголовки). Если вы используете Node.js с PostgreSQL, MySQL или любой другой реляционной БД, вы в зоне риска.
Как выглядит SQL-инъекция в JavaScript?
Представьте, что у вас есть форма логина, и вы пишете запрос так:
// ❌ ОПАСНО: уязвимость к SQL-инъекции
const username = req.body.username;
const password = req.body.password;
const query = `SELECT * FROM users WHERE username = '${username}' AND password = '${password}'`;
Теперь представьте, что злоумышленник вводит в поле username: admin' --. Запрос превращается в:
SELECT * FROM users WHERE username = 'admin' --' AND password = '';
-- — это комментарий в SQL. Всё, что после него, игнорируется. Хакер залогинился как админ без пароля. А если он введёт '; DROP TABLE users; --? Вы потеряете всю таблицу пользователей.
Защита: параметризованные запросы (prepared statements)
Лучший способ защиты от SQL-инъекций — никогда не вставлять пользовательские данные напрямую в строку запроса. Используйте параметризованные запросы. Вот как это делается в Node.js с библиотекой pg (PostgreSQL):
// ✅ БЕЗОПАСНО: параметризованный запрос
const { Pool } = require('pg');
const pool = new Pool();
app.post('/login', async (req, res) => {
const { username, password } = req.body;
// Знак $1 и $2 — это плейсхолдеры
const query = 'SELECT * FROM users WHERE username = $1 AND password = $2';
const values = [username, password];
try {
const result = await pool.query(query, values);
// обработка результата...
} catch (error) {
console.error('Ошибка запроса:', error);
}
});
Библиотека сама экранирует значения. Даже если злоумышленник передаст admin' --, это будет воспринято как обычная строка, а не как SQL-команда.
Дополнительные меры защиты
- Валидация на сервере: проверяйте тип и длину входных данных. Если ожидается email, убедитесь, что это действительно email.
- Принцип наименьших привилегий: не используйте root-пользователя БД в приложении. Создайте отдельного пользователя с правами только на нужные таблицы.
- ORM-библиотеки: используйте Prisma, Sequelize или TypeORM. Они автоматизируют параметризацию, но не забывайте, что и в них можно написать небезопасный код (например, используя
sequelize.queryс конкатенацией).
XSS (Cross-Site Scripting): Когда ваш сайт атакует сам себя
XSS — это атака, при которой злоумышленник внедряет вредоносный JavaScript-код на страницу, которую просматривают другие пользователи. Этот код может украсть куки, перенаправить на фишинговый сайт или даже перехватить нажатия клавиш. XSS бывает трёх типов: хранимый (в базе данных), отражённый (в URL) и DOM-based (на стороне клиента).
Пример уязвимости: комментарии без фильтрации
Представьте, что вы пишете блог, и пользователи могут оставлять комментарии. Если вы просто вставляете текст комментария в HTML:
// ❌ ОПАСНО: уязвимость к XSS
const comment = req.body.comment;
document.getElementById('comments').innerHTML += `<div>${comment}</div>`;
Злоумышленник может отправить комментарий:
<script>alert('Вы взломаны!')</script>
Скрипт выполнится в браузере каждого, кто откроет страницу. Вместо alert может быть отправка куков на сервер хакера.
Защита: экранирование и санитизация
Правило номер один: никогда не доверяйте пользовательскому вводу. Всегда экранируйте данные перед вставкой в HTML.
1. Используйте textContent вместо innerHTML
Самое простое и эффективное решение:
// ✅ БЕЗОПАСНО: textContent интерпретирует данные как текст
const comment = req.body.comment;
const div = document.createElement('div');
div.textContent = comment; // <script> станет текстом, а не кодом
document.getElementById('comments').appendChild(div);
2. Санитизация с помощью DOMPurify (для сложных случаев)
Если вам нужно разрешить пользователям вводить HTML (например, для форматирования текста), не пишите свой парсер — используйте проверенные библиотеки:
// ✅ Установка: npm install dompurify
import DOMPurify from 'dompurify';
const userInput = '<b>Жирный текст</b> <script>alert("xss")</script>';
const cleanInput = DOMPurify.sanitize(userInput);
// cleanInput = '<b>Жирный текст</b> ' (скрипт удалён)
document.getElementById('content').innerHTML = cleanInput;
3. Content Security Policy (CSP) — ваша последняя линия обороны
CSP — это HTTP-заголовок, который говорит браузеру, какие ресурсы можно загружать. Даже если злоумышленник внедрит скрипт, CSP заблокирует его выполнение:
// Настройка на сервере