ThinkPHP的input()默认不做过滤,仅去除首尾空白并依default_filter配置执行htmlspecialchars等转义;该行为属输出防护,非输入净化,不能防SQL注入,数字类参数须显式用intval等处理。

ThinkPHP 默认会对 input() 获取的数据做 htmlspecialchars 转义,但这只是输出防护,不是输入过滤;直接依赖它防 SQL 注入或存库前净化,会出问题。
input() 的默认过滤行为到底做了什么
ThinkPHP 的 input() 函数在未指定第三个参数时,会读取配置项 default_filter(默认值通常是 'htmlspecialchars'),对数据执行一次转义。这意味着:
- 输入
"<script>alert(1)</script>"→input('name')返回"<script>alert(1)</script>" - 这个结果仍是字符串,里面的 XSS payload 并未被删除,只是被编码成 HTML 实体
- 如果把这个结果直接拼进 SQL(如
"INSERT INTO ... VALUES ('" . input('name') . "')),依然可能被绕过或引发其他问题 -
default_filter支持数组,比如['strip_tags', 'htmlspecialchars'],但要注意执行顺序:先strip_tags再htmlspecialchars,中间没有 trim 或编码校验
过滤必须分清“输入净化”和“输出转义”
很多开发者误把 htmlspecialchars() 当成过滤函数用,但它本质是输出安全函数。真正需要输入层净化的场景,比如用户名、搜索关键词、文件名,应该主动清理非法字符:
- 只允许中文、字母、数字、下划线:
preg_replace('/[^\x{4e00}-\x{9fa5}a-zA-Z0-9_]/u', '', $str) - 去除所有控制字符和 null 字节(防解析异常):
preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $str) - 处理富文本字段不能靠
input()自动过滤,必须用HTMLPurifier白名单过滤后,再存库;模板里用{$content|raw}才安全 - 数据库写入一律用 PDO 预处理,不要把
input()结果直接拼 SQL
order/sort 等动态字段参数怎么安全处理
分页或列表排序常带 ?order=user_name&sort=desc 这类参数,这类字段名不能直接进 SQL 的 ORDER BY,否则会被注入(如传 user_name, (SELECT password FROM users)):
- 白名单校验最可靠:
in_array($order, ['user_name', 'created_at', 'status'], true) - 若字段来自数据库元信息,用正则限制:
preg_match('/^[a-zA-Z0-9_]{1,64}$/', $order) - 禁止用
filter_var($order, FILTER_SANITIZE_STRING)—— PHP 8.1+ 已移除该常量,且它不校验语义合法性 - Nginx/Apache 层可能提前 decode 或 rewrite 参数,调试时先看
$_SERVER['QUERY_STRING']和$_GET是否一致
容易被忽略的边界情况
中文、emoji、宽字符、URL 编码混合时,preg_replace 和 strlen 容易误判长度或截断:
- 用
mb_strlen($str, 'UTF-8')替代strlen()做长度限制 - 正则必须加
/u修饰符,否则无法正确匹配 Unicode 字符 -
rawurlencode()处理分页链接参数,别用urlencode()—— 后者把空格变+,会导致含加号的原始值(如搜索a+b)被错误还原 - 管理员后台输入也不能跳过过滤,信任来源是常见漏洞根源
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











