thinkphp 5.1+ 默认禁用危险函数但不自动防御sql注入,手动拼接sql(如db::query())仍高危;必须用参数绑定(如?占位符),禁用错误回显并最小化数据库权限。

ThinkPHP 5.1+ 默认已禁用 parse_str 和 eval 类危险函数,但 SQL 注入仍可能发生在手动拼接查询处
ThinkPHP 本身不自动拦截所有 SQL 注入,尤其当开发者绕过 ORM、直接用 Db::query() 或 Db::execute() 拼接字符串时,风险立刻出现。框架的「安全」只作用于它自己处理的查询逻辑(如 where() 链式调用),不覆盖你写的原始 SQL。
常见错误现象:Db::query("SELECT * FROM user WHERE id = " . input('id')) —— 这种写法哪怕开了 app_debug = false 也照常被注入。
- 必须用参数绑定替代字符串拼接,
Db::query("SELECT * FROM user WHERE id = ?", [input('id')])才安全 -
input()默认不做过滤,input('id/d')中的/d是类型转换,不是过滤;真正过滤需显式加/s或自定义验证规则 - 若用
Db::name('user')->where('id', input('id'))->find(),框架内部会自动参数化,无需额外操作
开启 sql_explain 并检查日志,快速定位未绑定参数的原生 SQL
ThinkPHP 的 sql_explain 配置不会阻止注入,但它能暴露哪些查询没走预处理——这是排查隐患最直接的线索。
在 config/database.php 中启用:
'sql_explain' => true,
'log' => [
'type' => 'File',
'level' => ['error', 'sql'],
],
然后访问一次含用户输入的列表页,去 runtime/log/ 下查 sql 日志。如果看到类似这样的记录:
SELECT * FROM article WHERE title LIKE '%{$_GET['q']}%'
说明该 SQL 是字符串拼接生成的,且未使用占位符,必须重构。
- 日志中出现
?或:name占位符,代表已参数化,基本安全 - 若用
think-swoole部署,需确认日志驱动支持并发写入,否则部分 SQL 可能丢失 -
sql_explain在生产环境建议关闭,仅用于阶段性审计
重写 Db::query() 入口,强制拦截含变量拼接的 SQL(适用于老项目补救)
无法逐个改造所有 Db::query() 调用?可以在数据库连接初始化后,用 think\db\Connection 的事件钩子做统一拦截。
在 app/common.php 或数据库中间件中加入:
use think\db\Connection;
Connection::maker(function ($connection) {
$originalQuery = $connection->query;
$connection->query = function ($sql, $bind = [], $master = false, $pdo = null) use ($originalQuery) {
if (is_string($sql) && preg_match('/\$\w+|{[^}]+}|[\'"]\s*\.\s*\$|\+\s*\$/', $sql)) {
throw new \Exception('Unsafe SQL detected: variable concatenation in raw query');
}
return $originalQuery($sql, $bind, $master, $pdo);
};
});
这段代码会扫描 SQL 字符串里是否含 $var、{xxx}、'xxx' . $y 等典型拼接痕迹,命中即抛异常。
- 正则不能 100% 覆盖所有拼接变体,但能捕获 90% 以上的明显风险写法
- 不要依赖此方案长期运行,它只是上线前的“兜底检测”,真正修复仍要回归参数绑定
- 若项目用了第三方扩展(如多租户插件)自行封装了
Db,需同步检查其调用链是否绕过该拦截
部署时禁用 debug 并关闭 show_error_msg,避免泄露 SQL 结构
即使 SQL 注入被成功执行,如果错误信息不回显,攻击者很难继续探测字段名或表结构。ThinkPHP 的错误页面默认显示完整 SQL 和 PDO 异常堆栈,这在生产环境是严重泄漏。
确认 app.php 中以下两项为明确关闭状态:
'app_debug' => false, 'show_error_msg' => false,
注意:app_debug = false 不等于安全,它只是隐藏错误细节;SQL 注入仍可造成数据篡改或盲注。
- 某些云平台(如阿里云虚拟主机)会强制开启
display_errors,需额外在.user.ini或php.ini中设display_errors = Off - 若用 Nginx,检查是否配置了
fastcgi_intercept_errors on,否则 PHP 错误仍可能透出到前端 - 就算关了错误显示,也要确保数据库账号权限最小化——比如只给
SELECT/INSERT,不给DROP或LOAD_FILE
真实风险往往藏在「看起来没问题」的旧代码里,尤其是那些用 Db::query() 处理搜索关键词、排序字段、动态表名的地方。参数绑定不是可选项,是唯一可靠的防护动作。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











