PDO: параметры защищают значения, но не имена столбцов

2 мин чтения

Что можно передавать параметрами PDO, как выбирать столбец сортировки и где отдельно проверять доступ к данным.

Что можно передавать параметром
  1. ЗначенияПодставляем через параметры PDO
  2. Имена полейВыбираем из разрешённого списка
  3. Связанные измененияОбъединяем в явную транзакцию

Параметр PDO хорошо подходит для имени пользователя или номера проекта, но не для имени столбца в ORDER BY. Параметры передают значения, а не части синтаксиса SQL. Разберём безопасную подстановку значений, выбор сортировки из разрешённого списка и отдельную проверку доступа к проекту.

Передаём значение параметром

$pdo = new PDO($dsn, $user, $password, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => false,
]);
$query = $pdo->prepare(
    'SELECT id, title FROM tasks WHERE project_id = :project'
);
$query->execute(['project' => $projectId]);
$rows = $query->fetchAll(PDO::FETCH_ASSOC);

Пример предполагает поддерживающий эти настройки драйвер и заранее подготовленное соединение. Сам идентификатор проекта всё равно должен проверяться на уровне доступа: защита от SQL-инъекции не означает, что пользователь вправе читать любой проект.

Сортировку выбираем из известных вариантов

$columns = ['date' => 'created_at', 'title' => 'title'];
$requestedSort = $_GET['sort'] ?? 'date';
$sort = is_string($requestedSort)
? ($columns[$requestedSort] ?? 'created_at')
: 'created_at'; $query = $pdo->prepare( "SELECT id, title FROM tasks WHERE project_id = :project ORDER BY $sort" ); $query->execute(['project' => $projectId]);

В SQL попадает значение из серверного массива, а не произвольный текст запроса. Для ASC и DESC нужен такой же ограниченный выбор. Не пытайтесь заменить имя столбца на :sort: сервер воспримет параметр как значение, а не как требуемую структуру SQL.

Когда нужна транзакция

Если несколько изменений должны пройти вместе, начните транзакцию, выполните операции и завершите её commit. При исключении откатите активную транзакцию и обработайте ошибку. Не держите её открытой, пока пользователь отвечает на вопрос или внешний сервис выполняет сетевой запрос.

Проверьте допустимую сортировку, неизвестное имя, параметр sort[]=title и чужой проект. Первые три случая должны выбрать разрешённый столбец или безопасное значение по умолчанию; чужой проект — получить отказ независимо от безопасности SQL. Подробности ошибки оставляйте в закрытом журнале; пользователю не нужны DSN, конфигурация и внутренние пути.

Источники и дальнейшее чтение