Как защититься от SQL-инъекций в Node.js приложении?

Ответ

Основной и самый надежный метод защиты — использование параметризованных запросов (Prepared Statements). При таком подходе данные пользователя передаются отдельно от кода запроса, и СУБД сама корректно их экранирует.

1. Использование драйверов и ORM с поддержкой параметризации:

  • Нативный драйвер mysql2 / pg (PostgreSQL):

    // УЯЗВИМЫЙ КОД (конкатенация строк)
    const query = `SELECT * FROM users WHERE email = '${email}'`;
    connection.query(query); // Если email = "' OR '1'='1", получим инъекцию
    
    // ЗАЩИЩЕННЫЙ КОД (параметризованный запрос)
    const safeQuery = 'SELECT * FROM users WHERE email = ?';
    connection.execute(safeQuery, [email]); // Драйвер корректно подставит значение
  • ORM Sequelize:

    // Sequelize автоматически использует параметризованные запросы
    const users = await User.findAll({
      where: {
        email: req.body.email // Безопасно!
      }
    });
  • Query Builder Knex.js:

    knex('users').where('email', '=', email).select('*'); // Безопасно

2. Дополнительные меры защиты:

  • Принцип наименьших привилегий: У учетной записи приложения в БД должны быть только необходимые права (обычно SELECT, INSERT, UPDATE, DELETE на конкретные таблицы, но не DROP, CREATE и т.д.).
  • Валидация и санация входных данных: Хотя это не заменяет параметризацию, строгая валидация (например, с помощью Joi или Zod) на уровне API отсекает заведомо некорректные данные.
  • Хранимые процедуры: Могут быть безопасны, если вызываются с параметрами, но в экосистеме Node.js используются реже.
  • Регулярное обновление зависимостей: Уязвимости могут быть в самих ORM или драйверах.

Главное правило: Никогда не доверяйте пользовательскому вводу и никогда не склеивайте SQL-запросы строковой конкатенацией или шаблонными литералами. Всегда используйте механизмы параметризации, предоставляемые вашим драйвером или ORM.

Ответ 18+ 🔞

Да что ж такое-то, блядь! Смотри, тут люди как будто с луны свалились — до сих пор строки конкатенируют, чтобы SQL-запросы собирать. Ёпта, ну это же пиздец, товарищи! Это как на машине без тормозов с горки ехать и надеяться, что в кусты мягко припаркуешься. Овердохуища уязвимостей открывается!

Так, слушай сюда, главное правило, которое надо на лбу вытатуировать: используй параметризованные запросы (Prepared Statements), ёб твою мать! Суть в чём? Данные от пользователя и сам код запроса — это две разные вселенные. Их скрещивать нельзя, а то получится манда с ушами. СУБД сама всё за тебя грамотно экранирует, если ты ей данные отдельно, как полагается, передашь.

1. Драйверы и ORM, которые за тебя головой думают:

  • Нативные драйверы mysql2 или pg (для PostgreSQL):

    // ЭТО ПИЗДЕЦ, НЕ ДЕЛАЙ ТАК! (конкатенация строк — прямой путь на свалку истории)
    const query = `SELECT * FROM users WHERE email = '${email}'`;
    connection.query(query); // Подставь сюда email = "' OR '1'='1" и получи инъекцию на блюдечке
    
    // А ВОТ ЭТО — КРАСОТА! (параметризованный запрос)
    const safeQuery = 'SELECT * FROM users WHERE email = ?';
    connection.execute(safeQuery, [email]); // Драйвер сам всё по уму сделает, подставит значение как надо
  • ORM Sequelize:

    // Sequelize — молодец, из коробки использует параметризацию, тебе вообще париться не надо
    const users = await User.findAll({
      where: {
        email: req.body.email // Абсолютно безопасно! Можно спать спокойно.
      }
    });
  • Query Builder Knex.js:

    knex('users').where('email', '=', email).select('*'); // Тоже безопасно, всё под капотом правильно обработает

2. Другие полезные штуки, чтобы спать ещё крепче:

  • Принцип наименьших привилегий: Зачем твоему приложению в базе права на DROP TABLE? Да низачем! Дай ему только SELECT, INSERT, UPDATE, DELETE на нужные таблицы и всё. Пусть сидит в своей песочнице.
  • Валидация данных на входе: Это, конечно, не панацея от SQL-инъекций, но отсеять откровенный бред — всегда полезно. Используй Joi, Zod или что там у тебя. Чисто для порядка.
  • Хранимые процедуры: Ну, теоретически, если их с параметрами вызывать, то тоже вариант. Но в мире Node.js это как-то не очень популярно, честно говоря.
  • Апдейти зависимости: Забыл обновить драйвер или ORM, а там дыра? Сам дурак, блядь. Следи за этим.

Итог, который надо выжечь в мозгу: Никогда, слышишь, НИКОГДА не доверяй тому, что пользователь ввёл в твою форму. И уж тем более не лепи из этого SQL-запросы, склеивая строки. Это уровень "Hello, World!" для начинающих пентестеров. Используй те инструменты параметризации, которые тебе твой драйвер или ORM предоставляет. И будет тебе счастье, а не хиросима в базе данных.