thinkphp 不自动防注入,需对用户输入按用途精准处理;input() 仅取值不过滤,where() 字符串拼接绕过预处理,json/动态字段需手动校验,防护须贯穿取值、校验、绑定、输出四环节。

ThinkPHP 本身不自动防注入,所有用户输入必须显式处理;不加过滤的 input()、字符串拼接 SQL、未校验的动态字段,是三个最常被攻破的入口。
input() 不等于过滤,只是取值代理
很多人以为 input('id') 会自动转义或做安全处理,其实它只做类型转换和默认值填充,返回原始字符串。攻击者传 id=1%27%20OR%20%271%27=%271,input('id') 就原样吐出 1' OR '1'='1,后续若直接拼 SQL 或进模板,立刻中招。
-
input('id')和input('id/s')都不防 SQL 注入,/s只表示「强制转字符串」,不是「XSS 过滤」 - 真正起作用的是第三个参数:
input('name', '', 'htmlspecialchars')做 HTML 输出防护,input('id', 0, 'intval')才防 SQL 注入 - 别依赖
default_filter全局配置——JSON 字段被htmlspecialchars编码后无法解析,富文本里的<img>会被干掉 - 验证器(
Validate)管业务规则,不管上下文用途;regex写成^[\w-.@]+$仍放过javascript:alert(1)
where() 字符串拼接 = 主动关掉防护
ThinkPHP 的 where() 方法只有在接收数组或闭包时才触发 PDO 预处理;一旦你写成 where('id = ' . input('id')),框架就跳过所有绑定逻辑,把拼好的字符串直接交给数据库执行。
- 错误现象:
SQLSTATE[42000]: Syntax error or access violation看似语法错,实则是单引号被闭合后跟了UNION SELECT - 安全写法必须用数组:
where(['id' => input('id/d'), 'status' => ['in', [0,1]]]),/d强制整型,框架自动生成占位符 - like 场景别拼
"%{$kw}%",改用['like', '%' . input('kw/s') . '%'],否则模糊查询也裸奔 -
order()、group()、having()同理:传字符串即危险,只接受白名单字段(如['id DESC', 'create_time ASC'])或固定枚举
JSON 请求、动态字段、EXP 表达式是隐藏雷区
这些场景下,框架不会自动识别上下文,更不会拦截魔术方法名或非法嵌套结构。前端传 {"__destruct":"x"} 或 {"id[where]":"1"},input('data/a') 会原样解析成 PHP 数组,后续若进 model->save() 或 array_merge(),可能触发反序列化或条件绕过。
- 接收 JSON 时禁用自动解析:
Request::post(false)拿原始 body,再用json_decode($raw, true)+ 手动校验键名深度 - 立刻剔除魔术方法:
array_filter($data, function($k) { return strpos($k, '__') !== 0; }, ARRAY_FILTER_USE_KEY) - EXP 表达式必须白名单:
$allowExp = ['eq','neq','gt','egt']; $exp = input('exp/s'); if (!in_array($exp, $allowExp)) $exp = 'eq'; - 动态表名/字段名绝不能来自 input,要用配置映射:
$tableMap = ['user'=>'user_info', 'order'=>'order_main']; $table = $tableMap[input('type/s')] ?? 'user_info';
最易被忽略的一点:防护不是单点动作,而是贯穿取值、校验、绑定、输出四环节的链式判断。同一个 input('callback'),进 SQL 要白名单,进 JS 回调要 in_array() 校验,进日志要 strip_tags(),进文件路径要 basename() —— 没有万能过滤函数,只有按用途精准处理。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











