JavaScript-безопасность: Как защитить сайт от SQL-инъекций и XSS

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Безопасность 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 заблокирует его выполнение:

// Настройка на сервере                

Блоги

Рекомендаций по книгам