防止 SQL 注入的最佳实践
SQL 注入是最常见的 Web 安全漏洞之一。本文介绍 PHP 中防止 SQL 注入的几种方法。
使用 PDO 预处理语句(推荐)
PDO 的预处理语句是防止 SQL 注入最安全的方式:
php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
// 命名参数
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id AND status = :status");
$stmt->execute([':id' => $userId, ':status' => $status]);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 位置参数
$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->execute([$name, $email]);使用 MySQLi 预处理语句
php
$mysqli = new mysqli('localhost', 'user', 'password', 'test');
$stmt = $mysqli->prepare("SELECT * FROM users WHERE email = ?");
$stmt->bind_param('s', $email); // 's' 表示字符串
$stmt->execute();
$result = $stmt->get_result();危险做法(不要这样做)
php
// ❌ 直接拼接 SQL - 极易受到 SQL 注入攻击
$query = "SELECT * FROM users WHERE id = '$userId'";
// ❌ 仅使用 addslashes() - 不够安全
$query = "SELECT * FROM users WHERE name = '" . addslashes($name) . "'";
// ❌ 依赖 magic_quotes(已废弃)IN 子句的参数化处理
IN 子句需要特殊处理:
php
function buildInClause($pdo, $column, array $values) {
$placeholders = implode(',', array_fill(0, count($values), '?'));
$sql = "$column IN ($placeholders)";
return ['sql' => $sql, 'values' => $values];
}
// 使用示例
$ids = [1, 2, 3, 4, 5];
$inClause = buildInClause($pdo, 'id', $ids);
$stmt = $pdo->prepare("SELECT * FROM users WHERE {$inClause['sql']}");
$stmt->execute($inClause['values']);LIKE 查询的安全处理
php
$search = '%' . $keyword . '%';
$stmt = $pdo->prepare("SELECT * FROM products WHERE name LIKE :search");
$stmt->execute([':search' => $search]);注意事项
- 永远不要 信任用户输入,包括
$_GET,$_POST,$_COOKIE等 - 预处理语句不仅对安全有益,还能提高查询性能(查询缓存)
- 对于动态表名、列名不能使用预处理,需要白名单校验:
php
$allowedTables = ['users', 'posts', 'comments'];
if (!in_array($table, $allowedTables)) {
throw new Exception('Invalid table name');
}