防止SQL注入的核心是使用预处理语句,确保用户输入作为纯数据传入;配合白名单校验动态SQL部分、严格输入验证、权限最小化与错误脱敏构成纵深防御。

防止PHP中的SQL注入,核心是让数据库永远分得清“代码”和“数据”。只要用户输入不参与SQL语句的拼接,就断了注入的根本路径。预处理语句是目前最可靠、最通用的解决方式,其他措施都是配合它形成纵深防御。
用PDO或MySQLi预处理语句代替字符串拼接
这是唯一被广泛认可能从根本上阻断SQL注入的方法。数据库先编译SQL结构(如 SELECT * FROM users WHERE id = ?),再把用户输入作为纯数据传入,完全不解析其内容。
- PDO推荐写法:连接时禁用模拟预处理,强制由数据库原生支持
$pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8mb4", $user, $pass, [<br> PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,<br> PDO::ATTR_EMULATE_PREPARES => false<br>]); - 占位符必须用
?或:name,不能把表名、字段名、排序方向(如ORDER BY)塞进去——这些要走白名单校验 - LIKE查询需在PHP中加通配符:
$search = "%{$_POST['kw']}%"; $stmt->execute([$search]);,不能写成WHERE name LIKE ?%
对非参数化部分做白名单校验
预处理管不了动态表名、列名、排序字段、分组方式等。这些必须提前定义合法值范围,运行时只允许从中选一个。
- 排序方向限制:
$sort = in_array($_GET['sort'], ['id', 'name', 'created_at']) ? $_GET['sort'] : 'id';
然后硬编码进SQL:"ORDER BY $sort DESC" - 状态枚举校验:
$status = in_array($_POST['status'], ['active', 'inactive', 'pending']) ? $_POST['status'] : 'active'; - 避免用
filter_var()校验字段名,它只适合邮箱、URL等标准格式,不适用于自定义业务字段
输入验证与类型约束要前置且严格
不是为了替代预处理,而是多一道过滤网,也能提前拦截明显异常请求,降低服务器压力。
立即学习“PHP免费学习笔记(深入)”;
- 数字类参数直接转整型:
$id = (int)$_GET['id'];或$id = filter_var($_GET['id'], FILTER_VALIDATE_INT) ?: 0; - 邮箱、URL等用
filter_var()验证格式,失败即拒绝:if (!filter_var($email, FILTER_VALIDATE_EMAIL)) die('Invalid email'); - 自由文本长度限制:
if (strlen($_POST['content']) > 1000) die('Too long'); - 不要用
addslashes()或mysqli_real_escape_string()——它们依赖字符集和上下文,容易绕过,已不推荐
权限最小化与错误信息脱敏
即使某处疏漏导致注入发生,也要让它干不了大事。
- 数据库账号只给
SELECT、INSERT、UPDATE、DELETE,禁用DROP、CREATE、ALTER、LOAD_FILE等高危权限 - 生产环境关闭错误显示:
ini_set('display_errors', 0);,同时开启错误日志:ini_set('log_errors', 1); - 捕获异常后返回泛化提示,不暴露表名、字段名、数据库版本等细节



















