唯一可行路径是值用?占位+USING传参、列名/表名白名单校验+反引号包裹;否则必致SQL注入或语法错误。

直接拼接字符串 + PREPARE/EXECUTE 是唯一可行路径,但值必须用 ? 占位 + USING 传参,列名/表名必须白名单校验 + 反引号包裹,否则必然触发 SQL 注入或语法错误。
MySQL 存储过程里怎么安全拼接 WHERE 条件
不能把用户输入直接拼进 SQL 字符串,哪怕只是字段名或操作符。比如 CONCAT('WHERE ', @col, ' = ?') 这种写法,只要 @col 是用户可控的,就等于敞开大门等攻击。
- 所有字段名、表名、排序字段、操作符(
=、LIKE、IN)必须先查白名单表或硬编码判断,再用CONCAT('`', @col_name, '`')包裹 - 所有值参数一律用
?占位,最后靠EXECUTE stmt USING @val1, @val2绑定,不拼字符串 - 空值或 NULL 参数要提前跳过整个条件分支,避免生成
AND name = NULL(它永远不返回数据) - 日期类参数建议先用
STR_TO_DATE(@input, '%Y-%m-%d')校验格式,失败则设为NULL,再进条件判断
为什么不能用 OR (@name IS NULL OR name = @name) 这种写法
看似简洁,实际会强制全表扫描,执行计划无法利用索引,大数据量时性能断崖式下跌。MySQL 优化器很难对这种“兜底式”条件做有效估算。
- 当
@name为NULL时,name = @name永远为FALSE,但优化器仍可能放弃索引走index_merge或直接ALL - 多个此类条件叠加(如 name + status + created_at),复合索引几乎失效
- 存储过程里用
IF判断显式拼接,生成的 SQL 是干净的WHERE 1=1 AND name = ?,执行计划稳定可预期
拼接时字段名含空格或保留字怎么办
MySQL 要求标识符含特殊字符时必须用反引号包裹,否则 PREPARE 直接报错 ERROR 1064。
- 不要依赖人工加反引号,统一用
CONCAT('`', @col_name, '`')动态包裹 - 白名单校验后立即转义,比如
@col_name := 'user name'→ 拼出`user name`,而不是user name - 避免用
CONCAT('SELECT ', @col, ' FROM ...'),应写成CONCAT('SELECT `', @col, '` FROM ...') - 表名同理,
CONCAT('FROM `', @table_name, '`')是安全底线
PREPARE 执行前必须检查的三件事
PREPARE 不校验 SQL 合法性,直到 EXECUTE 才报错,所以拼完 @sql 后得手动拦截明显危险信号。
- 用
INSTR(@sql, ';') > 0检查分号——动态 SQL 不该有多条语句 - 用
INSTR(LOWER(@sql), 'union select') > 0拦截典型注入关键词(大小写不敏感) - 用
INSTR(@sql, '--') > 0 OR INSTR(@sql, '/*') > 0检查注释符,防止绕过 - 如果任一检查命中,直接
LEAVE或抛出自定义错误,不执行PREPARE
真正难的不是拼字符串,而是让每个拼接环节都明确知道:这个变量是用户输入还是可信元数据,该走白名单还是参数绑定。漏掉任意一环,前面所有努力都归零。


















