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

Что такое SQL Injection?

1.8 Middle🔥 121 комментариев
#Безопасность#Базы данных и SQL

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

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

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

SQL Injection — критическая уязвимость безопасности

SQL Injection — это одна из самых опасных и распространённых уязвимостей. Входит в OWASP Top 10.

Что такое SQL Injection?

SQL Injection происходит, когда пользовательский ввод внедряется в SQL запрос без должной санитизации.

Классический пример уязвимости

// УЯЗВИМЫЙ КОД
const username = req.body.username; // "admin' --"
const password = req.body.password;

const query = `
  SELECT * FROM users 
  WHERE username = '${username}' 
  AND password = '${password}'
`;

// Результирующий SQL:
// SELECT * FROM users WHERE username = 'admin' --'
// Комментарий убирает проверку пароля!

Типы SQL Injection атак

1. Authentication Bypass

// Ввод: ' OR '1'='1
// Условие '1'='1' всегда true

2. Data Exfiltration (утечка данных)

// Ввод: ' UNION SELECT password FROM admin --
// Возвращает пароли администраторов

3. Data Destruction (удаление данных)

// Ввод: '; DROP TABLE users; --
// Удаляет таблицу!

Правильное решение: Prepared Statements

С использованием параметризованных запросов:

const pg = require('pg');
const pool = new pg.Pool();

async function authenticateUser(username, password) {
  const query = `
    SELECT * FROM users 
    WHERE username = $1 
    AND password = $2
  `;
  
  // Параметры передаются отдельно от SQL
  const result = await pool.query(query, [username, password]);
  return result.rows[0];
}

// Даже если пользователь введёт: ' OR '1'='1
// Это будет воспринято как строковой литерал

SQL Injection в Express приложении

Уязвивое:

app.get('/user/:id', async (req, res) => {
  const id = req.params.id;
  const query = `SELECT * FROM users WHERE id = ${id}`;
  
  // Атака: GET /user/1 OR 1=1
  // Вернёт всех пользователей!
  const result = await pool.query(query);
  res.json(result.rows);
});

Безопасное:

app.get('/user/:id', async (req, res) => {
  const id = req.params.id;
  const query = `SELECT * FROM users WHERE id = $1`;
  
  // Параметризованный запрос
  const result = await pool.query(query, [id]);
  res.json(result.rows);
});

Защита от SQL Injection

Checklist:

// 1. ВСЕГДА используй параметризованные запросы
await pool.query(`SELECT * FROM users WHERE id = $1`, [userId]);

// 2. НЕ конкатенируй пользовательский ввод
// await pool.query(`SELECT * FROM users WHERE id = ${userId}`); // НЕПРАВИЛЬНО

// 3. Валидируй ввод
const userId = parseInt(req.params.id);
if (isNaN(userId)) return res.status(400).json({ error: 'Invalid ID' });

// 4. Используй ORM когда возможно
const user = await User.findByPk(userId); // Безопасно

// 5. Минимизируй права БД пользователя
// Production: только SELECT, INSERT, UPDATE
// НЕ DROP, ALTER, CREATE

// 6. Логируй подозрительную активность

Примеры по разным БД

PostgreSQL:

await pool.query(
  'SELECT * FROM users WHERE email = $1 AND status = $2',
  [email, status]
);

MySQL:

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

Real-world пример: Правильная аутентификация

const bcrypt = require('bcrypt');

async function login(username, password) {
  // 1. Параметризованный запрос
  const result = await pool.query(
    'SELECT * FROM users WHERE username = $1',
    [username]
  );
  
  const user = result.rows[0];
  if (!user) {
    return { success: false, error: 'User not found' };
  }
  
  // 2. Сравниваем пароли (хешированные)
  const passwordMatch = await bcrypt.compare(password, user.password_hash);
  
  if (!passwordMatch) {
    return { success: false, error: 'Invalid password' };
  }
  
  return { success: true, user };
}

Key takeaway

  • SQL Injection = критическая уязвимость
  • Решение: Prepared Statements
  • Никогда не конкатенируй пользовательский ввод в SQL
  • Используй ORM когда возможно
  • Валидируй входные данные
  • Минимизируй права БД пользователя