Prepared Statements — защита от SQL-инъекций
Prepared Statements (параметризованные запросы) — техника, при которой SQL-запрос компилируется отдельно от данных, что полностью исключает SQL-инъекции: данные никогда не интерпретируются как код.
Зачем нужно
SQL-инъекция — одна из старейших и наиболее критичных уязвимостей: позволяет читать, изменять и удалять данные, обходить авторизацию. Prepared Statements — самый надёжный способ защиты: даже специальные символы (', ", --, ;) передаются как безопасные данные.
Где используется
- Любые SQL-запросы с пользовательским вводом (поиск, логин, фильтры)
- ORM-библиотеки (Sequelize, Prisma, Knex) — используют под капотом
- Хранимые процедуры с параметрами
Основной контент
Уязвимый код (конкатенация строк)
// УЯЗВИМО — никогда не делайте так!
const query = `SELECT * FROM users WHERE email = '${req.body.email}'`;
// Атакующий вводит: ' OR '1'='1
// Результат: SELECT * FROM users WHERE email = '' OR '1'='1'
// → Возвращает ВСЕХ пользователей
node-postgres (pg)
const { Pool } = require('pg');
const pool = new Pool();
// БЕЗОПАСНО — параметр $1 никогда не интерпретируется как SQL
const result = await pool.query(
'SELECT * FROM users WHERE email = $1',
[req.body.email]
);
// Несколько параметров
const user = await pool.query(
'SELECT id, name FROM users WHERE email = $1 AND active = $2',
[email, true]
);
mysql2 (Node.js)
const mysql = require('mysql2/promise');
const conn = await mysql.createConnection(config);
// Placeholder ? заменяется безопасно
const [rows] = await conn.execute(
'SELECT * FROM products WHERE category = ? AND price < ?',
[req.query.category, req.query.maxPrice]
);
Prisma (ORM)
// Prisma использует параметризацию автоматически
const user = await prisma.user.findUnique({
where: { email: req.body.email },
});
// Raw-запросы — используем Prisma.$queryRaw с шаблонными строками
const result = await prisma.$queryRaw`
SELECT * FROM users WHERE id = ${userId}
`;
// Prisma автоматически параметризует значение в template literal
Knex.js
// Knex builder автоматически параметризует значения
const users = await knex('users')
.where({ email: req.body.email, active: true })
.select('id', 'name');
Частые ошибки
- Конкатенация строк с данными пользователя в "простых" запросах — нет безопасных случаев
- Использование
$queryRawUnsafeв Prisma илиknex.rawс интерполяцией строк - Параметризация запроса, но динамическое формирование имён таблиц/колонок без whitelist
- Доверие данным из заголовков запроса (
User-Agent,X-Forwarded-For) без параметризации