← Назад к вопросам

Что такое SQL injection и как защитить Node.js приложение от него?

3.0 Senior🔥 261 комментариев
#Node.js и JavaScript#Базы данных и SQL#Безопасность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое SQL injection и как защитить Node.js приложение от него?

SQL Injection — это критическая уязвимость безопасности, при которой злоумышленник внедряет вредоносный SQL код в пользовательские входные данные.

Как происходит SQL Injection

const userId = req.query.id;
const query = `SELECT * FROM users WHERE id = ${userId}`;
// Если userId = "1 OR 1=1", то SELECT * FROM users WHERE id = 1 OR 1=1 вернёт ВСЕ пользователей

const username = req.body.username;
const query = `SELECT * FROM users WHERE username = '${username}' AND password = '...'`;
// Если username = "admin' --", то -- комментарий игнорирует остаток запроса

1. Защита через Parameterized Queries

const mysql = require('mysql2/promise');
const connection = await mysql.createConnection({ ... });

const userId = req.query.id;
const [rows] = await connection.execute(
  'SELECT * FROM users WHERE id = ?',
  [userId]
);

2. ORM (Object-Relational Mapping)

Prisma

const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

const userId = req.query.id;
const user = await prisma.user.findUnique({
  where: { id: parseInt(userId) },
});

const users = await prisma.user.findMany({
  where: { email: req.body.email },
});

TypeORM

const userRepository = getRepository(User);
const user = await userRepository.findOne({
  where: { id: userId },
});

const users = await userRepository
  .createQueryBuilder('user')
  .where('user.email = :email', { email: req.body.email })
  .getMany();

Sequelize

const user = await User.findByPk(userId);

const users = await User.findAll({
  where: { email: req.body.email },
});

const user = sequelize.query(
  'SELECT * FROM users WHERE email = ?',
  { replacements: [email], type: QueryTypes.SELECT }
);

3. Экранирование строк

const mysql = require('mysql2');
const userId = mysql.escape(req.query.id);
const query = `SELECT * FROM users WHERE id = ${userId}`;

4. Валидация входных данных

const { query, validationResult } = require('express-validator');

app.get('/users/:id',
  query('id').isInt().toInt(),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const userId = req.query.id;
    connection.query(
      'SELECT * FROM users WHERE id = ?',
      [userId],
      (error, results) => res.json(results)
    );
  }
);

5. Белые списки (white lists)

const allowedSortFields = ['name', 'email', 'created_at'];
const sortBy = req.query.sort;

if (!allowedSortFields.includes(sortBy)) {
  return res.status(400).json({ error: 'Invalid sort field' });
}

const query = `SELECT * FROM users ORDER BY ${sortBy}`;

6. Принцип наименьших привилегий

CREATE USER 'app_reader'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT ON myapp.* TO 'app_reader'@'localhost';

CREATE USER 'app_writer'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, UPDATE ON myapp.* TO 'app_writer'@'localhost';

7. Полный пример защиты

const express = require('express');
const { PrismaClient } = require('@prisma/client');
const { query, body, validationResult } = require('express-validator');

const app = express();
const prisma = new PrismaClient();

const handleValidationErrors = (req, res, next) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }
  next();
};

app.get('/users/:id',
  query('id').isInt().toInt(),
  handleValidationErrors,
  async (req, res) => {
    const user = await prisma.user.findUnique({
      where: { id: req.query.id },
    });
    res.json(user);
  }
);

app.post('/users',
  body('email').isEmail(),
  body('password').isLength({ min: 8 }),
  handleValidationErrors,
  async (req, res) => {
    const user = await prisma.user.create({
      data: {
        email: req.body.email,
        password: req.body.password,
      },
    });
    res.json(user);
  }
);

Чеклист защиты

  • Используй параметризованные запросы (?)
  • Используй ORM вместо raw SQL
  • Валидируй все входные данные
  • Используй белые списки
  • Применяй принцип наименьших привилегий
  • Не логируй чувствительные запросы
  • Используй контроль доступа
  • Регулярно обновляй зависимости
  • Проводи security audit
  • Используй WAF в production

Типичные ошибки

const query = `SELECT * FROM users WHERE id = ${req.query.id}`;
const query = `INSERT INTO users VALUES ('${username}', '${password}');

const [rows] = await connection.execute(
  'SELECT * FROM users WHERE id = ?',
  [req.query.id]
);

SQL Injection остаётся одной из самых опасных уязвимостей. Ключ к защите — использование параметризованных запросов и надежных библиотек.

Что такое SQL injection и как защитить Node.js приложение от него? | PrepBro