防止SQL注入唯一有效的是参数化查询,LIMIT必须用bindValue绑定整型并设硬上限,表名等动态部分须白名单校验。

直接拼接LIMIT参数就是开后门
用 $_GET['limit'] 转成整数再拼进 SQL,比如 "SELECT * FROM users LIMIT " . (int)$limit,看似“转了整型”,实则完全无效。攻击者只要传入 10; DROP TABLE users--,PHP 强转后变成 10,看起来安全——但前提是没人在别处偷偷拼了字符串。一旦代码里混用拼接和绑定(比如字段名用拼接、LIMIT 用绑定),整个防御就崩了。
PDO 中正确绑定 LIMIT 参数的唯一方式
LIMIT 是 SQL 的语法组成部分,不是值,但它支持问号占位符,且必须用 bindValue() 显式指定类型为 PDO::PARAM_INT。否则 PDO 可能把它当字符串处理,导致语法错误或绕过校验。
- ✅ 正确写法:
$stmt->bindValue(2, min((int)$limit, 100), PDO::PARAM_INT) - ❌ 错误写法:
$stmt->bindParam(2, $limit, PDO::PARAM_INT)(变量可能被中途改写) - ❌ 错误写法:
$stmt->bindValue(2, $limit)(缺类型,PDO 默认当字符串) - 硬上限必须设:比如最多 100 行,超出就截断,不报错也不放行
表名、排序方向、OFFSET 这些根本不能参数化
LIMIT 能绑,但 ORDER BY ?、SELECT * FROM ?、OFFSET ? 全部非法。MySQL/PostgreSQL/SQLite 都不支持对这些部分使用占位符。它们属于 SQL 结构,不是数据。
- 表名、字段名、
ASC/DESC必须走白名单校验,比如:in_array($order_field, ['id', 'name', 'created_at'], true) -
OFFSET同样要绑定 + 类型校验,但更危险:大偏移量会拖垮性能,建议改用游标分页 - 白名单必须服务器端做,前端传来的任何字符串都不能信
别被 max_rows 或 SQL_BUFFER_RESULT 欺骗
这两个 MySQL 配置常被误当作“防注入手段”,其实和安全零关系:max_rows 是优化器提示,不强制截断;SQL_BUFFER_RESULT 只是把结果缓到临时表释放锁。它们既不阻止多语句执行(; DROP TABLE),也不校验 SQL 结构。
真正起作用的只有一件事:所有用户输入,无论出现在 SQL 哪个位置,都必须要么走参数化绑定,要么过白名单。LIMIT 参数只是其中一环,单独拎出来防,意义不大——它暴露的是整个查询构造逻辑是否可信。

















