thinkphp 6 原生 sql 防注入关键在于参数绑定:必须用问号占位符+数组传参;命名参数需显式开启配置;严禁字符串拼接;动态标识符须白名单校验。

ThinkPHP 6 的 Db::query() 和 Db::execute() 支持原生 SQL 执行,但**是否安全不取决于方法本身,而取决于你传参的方式**。只要用对参数绑定机制,原生 SQL 同样可防注入;一旦字符串拼接,立刻失效。
✅ 必须用问号占位符 + 参数数组
TP6 底层调用 PDO 预处理,仅当 SQL 中含 ? 且第二个参数为数组时,才真正启用绑定。这是最稳妥、兼容性最强的写法。
Db::query("SELECT * FROM user WHERE status = ? AND type IN (?, ?)", [1, 'admin', 'vip'])Db::execute("UPDATE log SET msg = ? WHERE id = ?", [$msg, $id])- 参数顺序必须严格对应问号位置,不可错位或遗漏
⚠️ 命名参数需确认配置开启
命名占位符(如 :status)语义更清晰,但 TP6 默认不启用命名绑定支持。若直接使用会报错或退化为字符串拼接。
- 正确前提:已在数据库配置中设置
'params_bind' => true - 启用后可用:
Db::query("SELECT * FROM user WHERE status = :status", ['status' => 1]) - 未开启时强行使用,等同于裸奔——建议优先用问号方式,避免环境差异风险
❌ 绝对禁止字符串拼接
任何将用户输入直接嵌入 SQL 字符串的行为,无论是否加单引号、是否调用 input() 或 htmlspecialchars(),都不可信。
- 错误:
Db::query("SELECT * FROM user WHERE name = '" . input('name') . "'") - 错误:
Db::query("SELECT * FROM {$table} WHERE id = " . $id)(表名+值双重危险) - 错误:
Db::execute("INSERT INTO log VALUES ('" . date('Y-m-d') . "', '" . input('msg') . "')") - 这些写法绕过所有预处理,攻击者输入
admin' --或1 OR 1=1即可击穿
? 动态字段/表名必须白名单校验
ORDER BY、GROUP BY、UNION、表名、字段名等属于 SQL 结构标识符,无法参数化。必须靠白名单控制合法取值。
-
不安全:
$order = input('sort'); Db::query("SELECT * FROM user ORDER BY {$order} DESC") -
安全:
$allow = ['id', 'create_time', 'status']; $sort = in_array($order, $allow) ? $order : 'id'; Db::query("SELECT * FROM user ORDER BY {$sort} DESC") - 同理,动态表名也要先判断:
in_array($table, ['user', 'order', 'product'])
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











