ThinkPHP默认不自动过滤用户输入,必须由开发者按场景主动设计;推荐用Validate验证器统一做字段级过滤,支持链式处理且逻辑集中、复用性强,避免全局“一刀切”导致JSON或文件数据损坏。

ThinkPHP本身不自动过滤用户输入,安全过滤必须由开发者按场景主动设计。核心原则是:不依赖默认行为,区分输入、存储、输出三个环节,避免“一刀切”式全局处理。
用验证器统一做字段级过滤
这是最推荐的方式,既能校验又能清洗,逻辑集中、复用性强。验证器的filter属性会在验证通过后对指定字段执行链式处理,且只作用于实际提交的字段,不误伤JSON或文件数据。
- 定义验证规则时明确声明过滤方式,例如:
'title' => 'require|length:1,100',再在filter数组中写'title' => 'trim|htmlspecialchars' - 自定义过滤函数(如
safeHtml)需先注册到Validate::extendFilter(),不能直接写函数名 - 调用时必须实例化后使用
check()方法,静态调用validate()不会触发过滤 - 注意:过滤结果默认不修改原始数据,如需更新变量,得手动赋值或结合模型的
data()->filter()链式调用
按输出场景选过滤方式
同一个字段在不同环节需要不同处理:入库前不该转义HTML,但渲染到页面时必须转义;富文本要保留标签但剔除JS,纯文本则直接htmlspecialchars。
- 输出到HTML模板时,优先靠模板引擎自动转义(如
{$content}),禁用|raw除非内容已由HTMLPurifier清洗过 - 返回JSON接口的数据,绝不能用
htmlspecialchars,否则双引号变"导致解析失败 - 富文本字段建议集成
ezyang/htmlpurifier,配置白名单标签(如p,b,i,a[href|title]),关闭on*事件和javascript:协议 - 数值型参数入库前用
intval或filter_var($v, FILTER_VALIDATE_INT),别依赖input('id/d')——它只是类型转换,不是安全过滤
中间件做请求层预处理
适合基础清洗(如去空格、转义纯文本字段),但要谨慎使用,避免影响API或文件上传等非HTML场景。
立即学习“PHP免费学习笔记(深入)”;
- 创建中间件,在
handle()中读取$request->param(),对字符串值逐个调用trim(htmlspecialchars($v)),跳过数组、对象等复杂类型 - 用
$request->mergeParam($cleaned)注入清洗后数据,确保后续所有input()调用都基于新值 - 不要修改
$_GET/$_POST超全局变量,ThinkPHP 6 已不再依赖它们 - 注册到
app/middleware.php的http数组中,保证执行时机在路由解析之后
避开常见陷阱
很多安全问题源于对框架机制的误解或配置滥用。
-
default_filter在TP6已被移除,TP5.1中虽可用但不可靠——多入口、CLI下失效,且无法处理嵌套数组 -
input('name/s')里的/s只是类型断言,不是XSS防护;VAR_FILTERS配置在TP6+基本无效,还可能破坏JSON字段 - 不要在控制器里零散写
htmlspecialchars(input('xxx')),漏一个字段就留一个漏洞 - 数据库防注入必须用PDO预处理,别信
addslashes或手动拼SQL;模型allowField可防字段注入,但不能替代输入过滤



















