Принцип изложенный ниже есть общая директива не связанная PHP. Касаемо синтаксиса PHP и использования PDO (PHP Data Objects) как инструмента - это технический перевод.
=============================================
Правильный способ избежать атак путем внедрения SQL-кода, независимо от того, какую базу данных вы используете, состоит в том, чтобы отделить данные от SQL, чтобы данные оставались данными и никогда не интерпретировались парсером SQL как команды. Можно создать оператор SQL с правильно отформатированными частями данных, но если вы не полностью понимаете детали, всегда следует использовать подготовленные операторы и параметризованные запросы. Это операторы SQL, которые отправляются и анализируются сервером базы данных отдельно от каких-либо параметров. Таким образом, злоумышленник не сможет внедрить вредоносный SQL. В основном у вас есть два варианта для достижения этого:
===============================
Using PDO (for any supported database driver):
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute([ 'name' => $name ]);
foreach ($stmt as $row) {
// Do something with $row
}
=================================
Using MySQLi (for MySQL):
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// Do something with $row
}
===================================
Если вы подключаетесь к базе данных, отличной от MySQL, есть второй параметр, специфичный для драйвера, к которому вы можете обратиться (например, pg_prepare() и pg_execute() для PostgreSQL). PDO является универсальным вариантом.
===================================
Правильная настройка подключения PDO
Обратите внимание, что при использовании PDO для доступа к базе данных MySQL настоящие подготовленные операторы по умолчанию не используются. Чтобы исправить это, вы должны отключить эмуляцию подготовленных операторов. Пример создания соединения с использованием PDO:
====================================
$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8mb4', 'user', 'password');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
====================================
В приведенном выше примере режим ошибки не является строго обязательным, но рекомендуется его добавить. Таким образом, PDO будет информировать вас обо всех ошибках MySQL, вызывая исключение PDOException. Однако обязательной является первая строка setAttribute(), которая указывает PDO отключить эмулированные подготовленные операторы и использовать настоящие подготовленные операторы. Это гарантирует, что оператор и значения не будут проанализированы PHP перед их отправкой на сервер MySQL (что не даст возможному злоумышленнику возможности внедрить вредоносный SQL). Хотя вы можете установить кодировку в параметрах конструктора, важно отметить, что «старые» версии PHP (до 5.3.6) молча игнорировали параметр кодировки в DSN
====================================
Mysqli
Для mysqli мы должны следовать той же процедуре:
mysqli_report (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // Отчет об ошибках
$dbConnection = новый mysqli('127.0.0.1', 'имя пользователя', 'пароль', 'тест');
$dbConnection->set_charset('utf8mb4'); // кодировка
====================================
Объяснение
====================================
Оператор SQL, который вы передаете для подготовки, анализируется и компилируется сервером базы данных. Указав параметры (либо ?, либо именованный параметр, такой как :name в приведенном выше примере), вы сообщаете механизму базы данных, где вы хотите фильтровать. Затем, когда вы вызываете execute, подготовленный оператор объединяется с указанными вами значениями параметров.
Здесь важно то, что значения параметров объединяются с скомпилированным оператором, а не со строкой SQL. SQL-инъекция работает, обманывая скрипт, включая вредоносные строки, когда он создает SQL для отправки в базу данных. Таким образом, отправляя фактический SQL отдельно от параметров, вы ограничиваете риск получить что-то, чего вы не планировали.
Любые параметры, которые вы отправляете при использовании подготовленного оператора, будут обрабатываться просто как строки (хотя механизм базы данных может выполнить некоторую оптимизацию, поэтому параметры также могут быть числами, конечно). В приведенном выше примере, если переменная $name содержит 'Sarah'; DELETE FROM employees результатом будет просто поиск строки "'Sarah'; DELETE FROM employees", и вы не получите пустую таблицу.
Еще одним преимуществом использования подготовленных операторов является то, что если вы выполняете один и тот же оператор много раз в одном и том же сеансе, он будет проанализирован и скомпилирован только один раз, что даст вам некоторое увеличение скорости.
И так как вы спросили о том, как это сделать для вставки, вот пример (с использованием PDO):
========================================
$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
$preparedStatement->execute([ 'column' => $unsafeValue ]);
=========================================
Можно ли использовать подготовленные операторы для динамических запросов?
Хотя вы по-прежнему можете использовать подготовленные операторы для параметров запроса, структура самого динамического запроса не может быть параметризована, а некоторые функции запроса не могут быть параметризованы.
Для этих конкретных сценариев лучше всего использовать фильтр белого списка, который ограничивает возможные значения.
==========================================
// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
$dir = 'ASC';
}
=========================================