强制类型转换不能替代参数化查询,但作为sql注入前置过滤手段,在数值、布尔、日期字段上最轻量可靠——前提是转换发生在sql拼接前且覆盖所有入口(如$_get、$_post),否则仍存在注入风险。

强制类型转换不能替代参数化查询,但它在数值、布尔、日期类字段上是最轻量、最可靠的前置过滤手段——前提是它发生在SQL拼接之前,且覆盖所有入口。
intval() 和 (int) 在 PHP 中为什么能拦住部分 SQL 注入
当后端明确要求某个参数必须是整数(如 id、page、limit),用 intval() 或 (int) 转换后,所有非数字字符都会被剥离或归零:
-
intval("1' OR 1=1")→ 返回1,后面所有注入片段被丢弃 -
(int)"123abc"→ 得123;但(int)"abc123"→ 得0,若业务逻辑把0当作合法 ID,反而引入逻辑漏洞 - 如果数据库字段是
INT,而传入"1; DROP TABLE users",转换后只剩1,恶意语句根本进不了 SQL
关键点不是“转了就行”,而是:转换必须在任何 SQL 拼接动作之前完成,且对每个可能的入口($_GET、$_POST、$_REQUEST)都生效。
ThinkPHP 中 where 条件不加类型声明等于裸奔
ThinkPHP 默认不对 where() 的值做类型校验。比如写 $this->where('id', input('id'))->find(),传入 id=1 OR 1=1 会被原样拼进 SQL,生成 WHERE id = '1 OR 1=1' —— 看似字符串,实则已脱离语义控制。
- 正确做法是显式绑定类型:
where('id', input('id', 0, 'intval')),或在模型中定义protected $type = ['id' => 'integer'] - 开启
database.auto_convert配置后,框架会在解析时自动调用类型转换,但前提是数据库字段真实类型与 PHP 类型一致(比如 DB 字段是VARCHAR却设为integer,会转成0或空) - 绝对禁止写
whereRaw("id = ".$_GET['id']),这直接绕过所有框架层防护
JavaScript 的 parseInt() 不是后端安全的替身
前端用 parseInt("1' OR 1=1") 得到 1,看着很安全,但只要后端没做二次校验,这个“1”就可能被当作可信输入继续拼 SQL。
- 前后端都做类型转换 ≠ 双重保险,而是必须由后端兜底:前端可辅助体验,但不能替代后端验证
- ORM 如 Laravel Eloquent 自动绑定类型,但若手动写
DB::select("SELECT * FROM users WHERE id = ".$_GET['id']),前面加intval()也无效——因为拼接位置错了 - 日期字段用
strtotime()处理,遇到"2024-01-01; SELECT * FROM xxx"会返回false,可立即拒绝而非拼进 SQL
最容易被忽略的点是:类型转换必须覆盖所有参数入口,漏掉一个 $_POST['sort_by'] 或 $_GET['offset'],整个防御就形同虚设。更危险的是,开发者以为“转了就安全”,却没检查转换结果是否被业务逻辑误判为有效值(比如把 0 当作合法 ID)。











