PHP 怎么PHP SQL注入防御

wen PHP项目 2

本文目录导读:

PHP 怎么PHP SQL注入防御

  1. 最佳实践:使用 PDO 预处理语句
  2. 次选方案:使用 MySQLi 预处理语句
  3. 极不推荐:mysql_real_escape_string()
  4. 其他重要防御措施
  5. 总结:你现在应该怎么做?

在 PHP 中防御 SQL 注入的核心原则是:绝不信任用户输入,始终使用参数化查询(预处理语句)

以下是 PHP 防御 SQL 注入的几种标准方法,按推荐程度从高到低排列:

最佳实践:使用 PDO 预处理语句

PDO(PHP Data Objects)提供了一个统一的接口来访问多种数据库,并且天然支持预处理语句,能有效防止 SQL 注入。

<?php
// 1. 创建 PDO 连接
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$username = 'root';
$password = 'password';
try {
    $pdo = new PDO($dsn, $username, $password);
    // 设置错误模式为异常,便于调试
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    die('数据库连接失败:' . $e->getMessage());
}
// 2. 使用预处理语句(推荐)
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');
$stmt->execute([
    ':email' => $_POST['email'],
    ':status' => 'active'
]);
$user = $stmt->fetch();
// 或者使用问号占位符
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? AND status = ?');
$stmt->execute([$_POST['email'], 'active']);
$user = $stmt->fetch();
?>

为什么有效?
预处理语句将 SQL 逻辑与数据分离,数据库引擎先编译 SQL 模板(SELECT ... WHERE email = ?),然后再将用户输入作为纯数据绑定进去,即使用户输入包含 ' OR 1=1 --,它也不会被解析为 SQL 命令,而只是字符串值。

次选方案:使用 MySQLi 预处理语句

如果你必须使用 MySQLi,同样支持预处理语句。

<?php
$mysqli = new mysqli('localhost', 'root', 'password', 'testdb');
// 检查连接
if ($mysqli->connect_error) {
    die('连接失败:' . $mysqli->connect_error);
}
// 预处理语句
$stmt = $mysqli->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
$stmt->bind_param('ss', $name, $email);  // s=string, i=integer, d=double, b=blob
$name = $_POST['name'];
$email = $_POST['email'];
$stmt->execute();
echo "插入成功,ID: " . $stmt->insert_id;
$stmt->close();
?>

极不推荐:mysql_real_escape_string()

在旧的 mysql_* 函数中(已废弃),有人用 mysql_real_escape_string() 转义特殊字符,这种方法存在以下问题:

  • 容易忘记对某个输入进行转义
  • 编码设置不正确时仍可能被绕过
  • 无法防御某些复杂的注入场景(如宽字节注入)
// 已废弃,不推荐使用
$unsafe_input = mysql_real_escape_string($_POST['input']);
$sql = "SELECT * FROM users WHERE name = '$unsafe_input'";

你不需要使用这种方法,除非你在维护一个无法升级的 PHP 5.x 旧项目。


其他重要防御措施

最小权限原则

  • 为数据库连接创建专用的低权限用户,只赋予它必需的权限(如 SELECT、INSERT,避免 DROP、ALTER)。

永远不要直接拼接 SQL

// 危险!这是典型的 SQL 注入
$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
$result = mysqli_query($conn, $sql);

严格验证数据格式(输入白名单)

对于特定的数据类型(如整数、邮箱、枚举值),在存入数据库前先验证。

// ID 必须是整数
if (!ctype_digit($_GET['id'])) {
    die('无效的 ID');
}
// 或者使用 filter_var
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
if ($email === false) {
    die('无效的邮箱');
}

禁止直接显示数据库错误

在生产环境中,关闭 PHP 的 display_errors,并将错误记录到日志中,防止攻击者通过错误信息推断数据库结构。

使用 ORM(对象关系映射)框架

使用成熟的 ORM 如 Eloquent(Laravel)DoctrineRedBeanPHP,它们底层已经封装好了预处理语句,能大大降低人为犯错的风险。

// Laravel Eloquent 示例
$user = User::where('email', $request->input('email'))->first();
// 底层已自动使用参数绑定

你现在应该怎么做?

场景 推荐做法
新项目 使用 PDO 预处理语句,配合框架(Laravel/Symfony)自带 ORM
现有项目(PHP 7+) 将原生的 mysql_query 迁移到 PDOMySQLi
只需查询少量数据 使用 PDO + 命名参数(name 形式)
涉及批量插入/更新 继续使用预处理语句,循环绑定参数

一句话:从今天开始,在 PHP 中写 SQL 时,一律使用预处理语句。

抱歉,评论功能暂时关闭!