prepare语句不能直接拼接变量,因其解析静态sql模板,混入用户输入会导致sql注入;动态值须用?占位符配合execute using绑定,表名等标识符需白名单校验后字符串拼接。

PREPARE 语句为什么不能直接拼接变量
因为 PREPARE 解析的是静态 SQL 模板,一旦字符串里混入用户输入,MySQL 就会把它当语法一部分处理,而不是参数。比如写 CONCAT('SELECT * FROM users WHERE name = ''', @user_input, ''''),@user_input 是 'admin' OR '1'='1',结果就是完整执行恶意 SQL。
正确做法是把变动部分全交给 EXECUTE ... USING 绑定的参数处理,SQL 模板本身必须固定结构。
- 模板中只允许出现
?占位符,不能有变量名、列名、表名、ORDER BY 字段等动态内容 - 表名/列名/排序字段等需动态的部分,只能用字符串拼接(但必须严格白名单校验)
-
PREPARE后的语句名(如stmt)是会话级的,重复PREPARE stmt会报错MySQL Error 1243: Unknown prepared statement handler,要先DEALLOCATE PREPARE stmt
如何安全地绑定多个参数并传入 EXECUTE
USING 后只能跟用户变量(@var),不能直接传字面量或表达式。这些变量必须提前 SET 好,且类型最好和目标字段一致,否则隐式转换可能绕过索引或引发截断。
例如查询范围时用两个日期参数:
SET @start = '2024-01-01'; SET @end = '2024-12-31'; SET @sql = 'SELECT * FROM orders WHERE created_at BETWEEN ? AND ?'; PREPARE stmt FROM @sql; EXECUTE stmt USING @start, @end;
- 参数个数必须和
?数量严格一致,多一个少一个都会报错MySQL Error 1210: Incorrect arguments to EXECUTE - 字符类参数建议显式加
CAST(? AS CHAR)防止 collation 冲突,尤其跨表 JOIN 时 - NULL 参数可以直接传
NULL(即SET @x = NULL),但不能传字符串'NULL'
表名动态化时为什么不能用 ? 占位符
因为 ? 只能代表“值”,不能代表“标识符”(identifier)。MySQL 解析器在 PREPARE 阶段就要求表名、列名、数据库名这些必须是合法标识符,而 ? 在此时还没被替换,直接报错 MySQL Error 1064: You have an error in your SQL syntax。
真要动态表名,只能拼接,但必须限制来源:
- 从白名单枚举中取:比如
SET @table = IF(@type = 'user', 'users_v2', IF(@type = 'order', 'orders_archive', 'users')); - 用正则过滤:如
SELECT @table := REGEXP_REPLACE(@raw, '[^a-zA-Z0-9_]', ''),再加长度和首字符检查(不能以数字开头) - 绝不能用
CONCAT('SELECT * FROM ', @user_table)且不对 @user_table 做任何校验
存储过程中用 PREPARE 容易漏掉的释放和作用域问题
存储过程里反复 PREPARE 不释放,会快速耗尽会话的 prepared statement 限额(默认 max_prepared_stmt_count=16382),触发 MySQL Error 1461: Can't create more than max_prepared_stmt_count statements。
- 每次
PREPARE前加IF EXISTS (SELECT 1 FROM information_schema.prepared_statements WHERE STATEMENT_NAME = 'stmt') THEN DEALLOCATE PREPARE stmt; END IF;(MySQL 8.0+ 才有该视图) - 更稳妥的是统一用
DEALLOCATE PREPARE IF EXISTS stmt(5.7.20+ 支持) - 注意:存储过程内定义的用户变量(
@var)是会话级的,跨过程调用可能被污染;建议每个过程都SET @var = DEFAULT或明确初始化
动态 SQL 的边界其实很窄:值 → 用 ? + USING;结构 → 必须白名单 + 严格过滤。漏掉任一环,防注入就形同虚设。










