php sql注入修复的核心是参数化查询,必须用pdo或mysqli预处理语句替代字符串拼接,并对动态字段、表名等采用白名单校验,禁用错误回显与不安全过滤函数。

PHP SQL注入漏洞的核心问题是把用户输入当成了SQL代码执行,而不是当作纯数据处理。修复不能靠“加个转义”或“拦几个关键字”应付,必须从数据与代码分离的根本原则出发。
最可靠、最彻底的修复方式只有一种:参数化查询(Prepared Statements)
用 PDO 或 MySQLi 预处理语句替代字符串拼接
所有涉及用户输入的 SQL 操作,都必须拆成“模板 + 参数”两部分:
// ❌ 危险:直接拼接
$username = $_POST['username'];
$sql = "SELECT * FROM users WHERE username = '$username'";
mysqli_query($conn, $sql);
// ✅ 安全:PDO 预处理(推荐)
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$result = $stmt->fetchAll();
// ✅ 安全:MySQLi 预处理
$stmt = $mysqli->prepare("SELECT * FROM users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result()->fetch_all();
-
"s"表示字符串,"i"表示整数,"d"表示浮点数——类型绑定能进一步防止绕过。 - 不要混用:别在
prepare()后还用mysqli_query()执行拼接语句。
动态字段名、表名、ORDER BY、LIMIT 怎么办?
这些位置无法使用占位符,必须靠白名单校验:
// ✅ 安全:排序字段白名单
$allowed_sorts = ['id', 'name', 'created_at', 'price'];
$sort = $_GET['sort'] ?? 'id';
$sort = in_array($sort, $allowed_sorts, true) ? $sort : 'id';
// ✅ 安全:分页限制范围
$limit = (int)($_GET['limit'] ?? 20);
$limit = max(10, min(100, $limit));
$sql = "SELECT * FROM products ORDER BY {$sort} DESC LIMIT {$limit}";
- 禁止用正则“过滤掉
;或--”,攻击者可用/**/、换行、十六进制编码绕过。 - 表名、字段名若来自用户(如多租户系统),必须映射为内部预设键值,不直传。
输入类型强制校验,不依赖过滤函数
- 数字参数:用
(int)、filter_var($x, FILTER_VALIDATE_INT)或is_numeric()+ 范围检查。 - 字符参数:不靠
addslashes()或mysql_real_escape_string()(已废弃且不可靠),而是交给预处理。 - 特殊场景(如搜索关键词):用
LIKE时也走绑定:$stmt = $pdo->prepare("SELECT * FROM posts WHERE title LIKE ?"); $stmt->execute(["%{$keyword}%"]);
关闭错误回显,避免报错注入
生产环境必须禁用数据库错误暴露:
// PHP 配置中设置
ini_set('display_errors', '0');
error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
// 数据库连接时关闭模拟预处理(PDO)
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_EMULATE_PREPARES => false, // 关键!
]);
// 所有查询包裹 try/catch,只记日志,不输出错误
try {
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
} catch (PDOException $e) {
error_log('DB Error: ' . $e->getMessage());
die('系统繁忙,请稍后重试');
}
框架用户注意特定风险点
- ThinkPHP:避免
where('field', 'exp', $input);升级到 5.1.31+ 或手动加固parseKey()和parseData(),对数组键名校验。 - Laravel:不用
DB::raw()拼接用户输入;orderBy()第二个参数必须是布尔值,字段名走白名单。 - 自研模板引擎(如 CMS):禁止在
{pboot:list where="id={$_GET['id']}"}这类标签中直插变量;所有模板变量需先经类型转换或白名单过滤。
不复杂但容易忽略。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











