在 PHP 中防止 SQL 注入的最佳实践是使用 预处理语句(Prepared Statements),而不是直接拼接 SQL 字符串。以下是具体方法和示例:


方法 1:使用 PDO(推荐)

// 连接数据库
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8';
$user = 'root';
$pass = '';
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => false // 禁用预处理模拟
];

try {
    $pdo = new PDO($dsn, $user, $pass, $options);
} catch (PDOException $e) {
    die("数据库连接失败: " . $e->getMessage());
}

// 预处理语句示例
$username = $_POST['username'];
$password = $_POST['password'];

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$stmt->execute();

$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

方法 2:使用 MySQLi

// 连接数据库
$mysqli = new mysqli('localhost', 'root', '', 'test');
if ($mysqli->connect_error) {
    die("数据库连接失败: " . $mysqli->connect_error);
}

// 预处理语句示例
$username = $_POST['username'];
$password = $_POST['password'];

$stmt = $mysqli->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param('ss', $username, $password); // 'ss' 表示两个字符串参数
$stmt->execute();

$result = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);

需要注意的是:

  1. 为什么预处理安全?
    预处理通过将 SQL 逻辑与数据分离,确保用户输入始终被当作数据处理(而非 SQL 代码)。

  2. 避免过时方法

    • mysql_real_escape_string():仅对字符串有效,需配合正确字符集。

    • addslashes():不完全可靠,已被淘汰。

    • magic_quotes_gpc:PHP 5.4+ 已移除,绝对不要依赖。

  3. 其他过滤建议

    • 使用 filter_input() 验证输入格式:

      $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    • 对输出到 HTML 的内容使用 htmlspecialchars() 防止 XSS。


总结

  • 优先使用预处理语句(PDO 或 MySQLi)。

  • 避免手动拼接 SQL 字符串。

  • 确保数据库连接字符集正确(如 utf8mb4)。

  • 保持 PHP 和数据库驱动更新至最新版本。

       如果你在维护旧代码且无法使用预处理,至少要做到:

$username = $mysqli->real_escape_string($_POST['username']);
$query = "SELECT * FROM users WHERE username = '$username'";