Разберитесь с этим PHP‑фрагментом: '$query = "SELECT * FROM users WHERE id=".$_GET["id"]; $res = mysql_query($query);' — какие риски здесь, как это исправить и какие лучшие практики по работе с вводом пользователя должны соблюдаться
Коротко — что не так, как исправить и лучшие практики. Риски - SQL‑инъекция: если пользователь передаст, например, `?id=1 OR 1=1`, запрос станет `SELECT * FROM users WHERE id=1 OR 1=1` и вернёт/изменит не то, что нужно. (пример инъекции: `1 OR 1=11=11=1`). - Использование устаревшего `mysql_*` API (deprecated/removed в PHP7) — нет поддержки подготовленных выражений. - Утечка данных/ошибок при выводе подробных сообщений об ошибках. - Чрезмерные привилегии БД (если учётная запись имеет права больше, чем нужно). Как исправить (практическое) 1) Никогда не подставлять вход напрямую в SQL. Использовать подготовленные выражения (parameterized queries). Пример на PDO: $pdo = new PDO('mysql:host=host;dbname=db;charset=utf8mb4', $user, $pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false, ]); $stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id'); $stmt->execute([':id' => $_GET['id']]); $user = $stmt->fetch(); Пример на MySQLi: $stmt = $mysqli->prepare('SELECT * FROM users WHERE id = ?'); $stmt->bind_param('i', $_GET['id']); // 'i' — integer $stmt->execute(); $res = $stmt->get_result(); 2) Быстрый частичный фикс — строгая фильтрация/кастинг (только если ожидается целое): $id = (int) $_GET['id']; // потом безопасно: SELECT * FROM users WHERE id = ? Но это не заменяет подготовленные выражения для строковых/сложных запросов. Лучшие практики по работе с вводом пользователя - Всегда использовать parameterized queries / prepared statements (PDO или MySQLi). - Валидировать и фильтровать вход по «белому списку»: тип, длина, формат (регулярные выражения, filter_var и т.д.). - Ограничивать привилегии DB‑пользователя (минимально необходимые права). - Устанавливать кодировку соединения (utf8mb4) и отключать эмуляцию подготовленных выражений (PDO::ATTR_EMULATE_PREPARES = false). - Не выводить подробные SQL‑ошибки пользователю; логировать их безопасно. - Ограничивать максимальную длину и сложность входа, применять rate limiting. - Использовать HTTPS, проверять CSRF там, где это нужно, и применять XSS‑защиту при выводе данных. - Регулярно обновлять библиотеке и слушать сканеры безопасности / код‑ревью. Краткое резюме: замените конструкцию $query = "SELECT * FROM users WHERE id=".$_GET["id"]; на использование подготовленного запроса (PDO/Mysqli) + валидацию входа и минимизацию прав доступа.
Риски
- SQL‑инъекция: если пользователь передаст, например, `?id=1 OR 1=1`, запрос станет `SELECT * FROM users WHERE id=1 OR 1=1` и вернёт/изменит не то, что нужно. (пример инъекции: `1 OR 1=11=11=1`).
- Использование устаревшего `mysql_*` API (deprecated/removed в PHP7) — нет поддержки подготовленных выражений.
- Утечка данных/ошибок при выводе подробных сообщений об ошибках.
- Чрезмерные привилегии БД (если учётная запись имеет права больше, чем нужно).
Как исправить (практическое)
1) Никогда не подставлять вход напрямую в SQL. Использовать подготовленные выражения (parameterized queries). Пример на PDO:
$pdo = new PDO('mysql:host=host;dbname=db;charset=utf8mb4', $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => $_GET['id']]);
$user = $stmt->fetch();
Пример на MySQLi:
$stmt = $mysqli->prepare('SELECT * FROM users WHERE id = ?');
$stmt->bind_param('i', $_GET['id']); // 'i' — integer
$stmt->execute();
$res = $stmt->get_result();
2) Быстрый частичный фикс — строгая фильтрация/кастинг (только если ожидается целое):
$id = (int) $_GET['id'];
// потом безопасно: SELECT * FROM users WHERE id = ?
Но это не заменяет подготовленные выражения для строковых/сложных запросов.
Лучшие практики по работе с вводом пользователя
- Всегда использовать parameterized queries / prepared statements (PDO или MySQLi).
- Валидировать и фильтровать вход по «белому списку»: тип, длина, формат (регулярные выражения, filter_var и т.д.).
- Ограничивать привилегии DB‑пользователя (минимально необходимые права).
- Устанавливать кодировку соединения (utf8mb4) и отключать эмуляцию подготовленных выражений (PDO::ATTR_EMULATE_PREPARES = false).
- Не выводить подробные SQL‑ошибки пользователю; логировать их безопасно.
- Ограничивать максимальную длину и сложность входа, применять rate limiting.
- Использовать HTTPS, проверять CSRF там, где это нужно, и применять XSS‑защиту при выводе данных.
- Регулярно обновлять библиотеке и слушать сканеры безопасности / код‑ревью.
Краткое резюме: замените конструкцию
$query = "SELECT * FROM users WHERE id=".$_GET["id"];
на использование подготовленного запроса (PDO/Mysqli) + валидацию входа и минимизацию прав доступа.