Зачем использовать плейсхолдеры (prepared statements) в SQL-запросах?

Ответ

Плейсхолдеры (placeholders) — это специальные маркеры в SQL-запросе, которые используются для безопасной подстановки пользовательских данных. Этот механизм является частью Prepared Statements (подготовленных выражений) и является стандартом для работы с базами данных.

Главная цель — предотвращение SQL-инъекций

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

Неправильно (уязвимо к инъекциям):

// email может содержать вредоносный код, например: ' OR 1=1; --
email := "' OR 1=1; --"
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
db.QueryRow(query) // Опасно!

Правильно (безопасно):

email := "user@example.com"
// Используем плейсхолдер `?` (синтаксис зависит от драйвера)
query := "SELECT * FROM users WHERE email = ?"
// Драйвер БД сам безопасно подставит значение
db.QueryRow(query, email)

При использовании плейсхолдеров данные передаются на сервер БД отдельно от самого SQL-запроса. Сервер сначала получает и парсит шаблон запроса, а затем применяет к нему данные, обрабатывая их как литералы, а не как часть исполняемого кода. Это полностью исключает возможность SQL-инъекции.

Дополнительные преимущества

  1. Производительность: СУБД может кешировать план выполнения для подготовленного запроса. При повторном вызове того же запроса с другими параметрами не тратится время на его повторный парсинг и анализ.
  2. Читаемость и поддержка: Код становится чище, так как логика запроса отделена от данных.
  3. Корректная обработка типов: Драйвер database/sql берет на себя заботу о правильном экранировании спецсимволов и преобразовании типов Go в соответствующие типы SQL.

Важно: Синтаксис плейсхолдеров

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

  • PostgreSQL: Позиционные, $1, $2, и т.д. INSERT INTO users (name, age) VALUES ($1, $2)
  • MySQL, SQLite: Знак вопроса ?. UPDATE users SET name = ? WHERE id = ?
  • Oracle: Именованные, :name, :id. SELECT * FROM users WHERE name = :name