本文目录导读:

在PHP项目中防止SQL注入,最有效、最推荐的方法是使用预处理语句(Prepared Statements),它可以确保SQL语句的结构不会被用户输入的数据改变。
以下是几种核心的防护方法,按推荐程度从高到低排列:
使用 PDO 或 MySQLi 的预处理语句(最佳实践)
预处理语句将SQL模板与数据分开,即使数据中包含恶意SQL代码,它也只会被当作字符串处理,而不会改变SQL的逻辑。
方法 A:PDO(推荐,支持多种数据库)
<?php
// 1. 创建 PDO 连接(设置错误模式和模拟预处理关闭)
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8mb4';
$pdo = new PDO($dsn, 'username', 'password', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // 关闭模拟预处理,使用真实的数据库预处理
]);
// 2. 准备SQL语句(使用 ? 占位符 或 :name 命名占位符)
$sql = "SELECT * FROM users WHERE username = :username AND password = :password";
$stmt = $pdo->prepare($sql);
// 3. 绑定参数并执行
$stmt->execute([
':username' => $_POST['username'],
':password' => hash('sha256', $_POST['password']), // 示例:密码不要明文存储
]);
// 4. 获取结果
$user = $stmt->fetch();
?>
关键点:
PDO::ATTR_EMULATE_PREPARES => false:让 PHP 使用数据库真实的预处理功能,而不是模拟,这能更彻底地防止注入。- 使用 或
name占位符:不要手动拼接变量。
方法 B:MySQLi(面向对象风格)
<?php
// 1. 创建 MySQLi 连接
$conn = new mysqli('localhost', 'username', 'password', 'test');
// 检查连接
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
// 2. 准备SQL语句
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $conn->prepare($sql);
// 3. 绑定参数(s = 字符串,i = 整数,d = 双精度,b = 二进制)
$username = $_POST['username'];
$stmt->bind_param("s", $username);
// 4. 执行
$stmt->execute();
// 5. 获取结果
$result = $stmt->get_result();
$user = $result->fetch_assoc();
// 6. 关闭连接
$stmt->close();
$conn->close();
?>
永远不要手动转义(贬义做法)
不推荐的做法是使用 mysqli_real_escape_string()。
- 原因:它容易出错,需要正确设置数据库连接编码(如
set_charset('utf8')),且容易忘记处理某些字符,一旦忘记使用,就不安全。 - 放弃
addslashes()和mysqli_real_escape_string(),改用预处理语句。
输入验证与白名单(补充措施)
对于数值类型或固定选项,验证数据类型可以增加一层防护。
// 示例:确保 ID 是整数
$id = (int)$_GET['id']; // 强制转换为整数,去掉所有非数字字符
// 示例:确保排序字段在白名单中
$allowedSort = ['name', 'date', 'id'];
$sort = $_GET['sort'] ?? 'id';
if (!in_array($sort, $allowedSort)) {
$sort = 'id'; // 默认值,防止 SQL 注入
}
注意:验证不能替代预处理语句,只能作为额外保险。
防范 ORDER BY、LIMIT 等特殊场景
有些SQL子句(如 ORDER BY、LIMIT、表名、列名)不能使用预处理语句绑定参数,此时需要严格的白名单验证:
$direction = strtoupper($_GET['dir']); // ASC 或 DESC
if (!in_array($direction, ['ASC', 'DESC'])) {
$direction = 'ASC';
}
$sql = "SELECT * FROM products ORDER BY price $direction";
其他安全措施(防御纵深)
- 最小权限原则:数据库用户只授予必要的权限,应用用户通常只要
SELECT、INSERT、UPDATE、DELETE,不需要DROP、CREATE。 - 隐藏错误信息:生产环境关闭
display_errors,避免 SQL 报错信息被攻击者利用。 - 使用 ORM:成熟的 ORM(如 Doctrine、Eloquent)多数情况下会自动使用预处理语句,但如果使用
raw查询方法(如DB::raw()),风险仍在,仍需手动处理。
关键原则
| 做法 | 是否推荐 | 说明 |
|---|---|---|
| PDO 预处理语句 | ✅ 强烈推荐 | 最佳实践,兼容多数据库 |
| MySQLi 预处理语句 | ✅ 推荐 | 只用于 MySQL |
mysqli_real_escape_string() |
❌ 不推荐 | 容易遗漏,有编码风险 |
addslashes() |
❌ 禁止 | 完全不可靠 |
| 输入验证(类型、白名单) | ✅ 补充使用 | 不能替代预处理,但可以增加安全性 |
一句话总结:凡是SQL查询中出现用户输入的地方(包括GET参数、POST数据、Cookie、文件上传名等),都应当使用预处理语句插入数据。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播