最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在SQL存储过程中如何安全地传递参数防止注入攻击?
时间:2026-07-09 10:17:46 编辑:袖梨 来源:一聚教程网
存储过程参数必须直接使用,严禁拼接进动态SQL;安全做法是参数直引用或通过USING绑定值,结构部分(如表名)须经元数据白名单校验,且执行账号权限需严格限制。
存储过程参数必须直接用,不能拼进动态SQL
MySQL 存储过程中,只要参数不参与字符串拼接,就天然免疫注入。比如 SELECT * FROM users WHERE id = p_user_id 这种写法,p_user_id 是输入参数,MySQL 会按类型安全绑定,无需额外处理。
常见错误是把参数拿去拼 CONCAT():SET @sql = CONCAT('SELECT * FROM users WHERE id = ', p_user_id)——哪怕 p_user_id 是整数,也打开了注入口。一旦传入 1 OR SLEEP(5),查询就变成全表扫描加延迟。
- 所有 WHERE、JOIN、HAVING 条件中,直接引用参数名,别转成字符串再拼
- 避免在存储过程里用
PREPARE+EXECUTE包裹含参数的字符串,除非真需要动态结构 - 如果参数是字符串类型,注意 MySQL 默认不会自动加引号,但类型校验(如
INT参数传入字母会报错)本身已是第一道过滤
必须用动态SQL时,只拼结构,值全走 USING
当确实要构建可变表名或字段列表(比如分表查询),PREPARE + EXECUTE 不可避免,但必须严格分离:结构部分(表名、列名)靠白名单或系统视图校验;数据部分(WHERE 值、LIMIT 数)全部通过 USING 绑定。
错误示例:SET @sql = CONCAT('SELECT * FROM ', p_table_name, ' WHERE status = ''', p_status, '''') —— 这里 p_table_name 和 p_status 都没防住。
- 正确做法:
SET @sql = CONCAT('SELECT * FROM ', (SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'mydb' AND TABLE_NAME = p_table_name LIMIT 1)); - 然后
PREPARE stmt FROM @sql; EXECUTE stmt USING p_status; -
USING只支持值绑定,不支持标识符,所以p_status安全,而表名校验必须前置
SQL Server 存储过程必须用 sp_executesql,禁用 EXEC(@sql)
SQL Server 中 EXEC(@sql) 等价于把整个字符串当命令执行,用户输入一旦混入,立刻执行任意语句。而 sp_executesql 支持参数化,值不参与语法解析,是唯一合规路径。
典型翻车点:有人以为给 @sql 加了 QUOTENAME() 就安全,但若参数本身是拼出来的:N'@p nvarchar(50) = ''' + @user_input + '''',还是回到了字符串拼接的老路。
- 必须写成:
EXEC sp_executesql @sql, N'@name nvarchar(50)', @name = @user_input -
@sql字符串里只能出现占位符@name,不能出现任何用户输入的字面量 - 类型声明(
N'@name nvarchar(50)')必须精确匹配,否则可能触发隐式转换导致截断或失败
动态对象名(表/列)必须白名单校验,escapeId() 或 QUOTENAME() 是补救不是替代
connection.escapeId()(Node.js mysql2)或 QUOTENAME()(SQL Server)能转义反引号或方括号,但它们不验证对象是否存在、是否合法。攻击者传入 user`; DROP TABLE logs; --,escapeId() 会输出 `user`; DROP TABLE logs; --`,依然执行两句话。
真正可靠的方式是查元数据表确认存在且归属预期范围:
- MySQL:
SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'app_db' AND TABLE_NAME = p_table_name - SQL Server:
IF NOT EXISTS (SELECT 1 FROM sys.tables t JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE s.name = 'dbo' AND t.name = @table_name) THROW 50000, 'Invalid table', 1; - 别依赖
OBJECT_ID(@name)单独判断——它对非法名称返回 NULL,但无法区分不存在和恶意截断
最易被忽略的是权限上下文:即使表名校验通过,执行账号也必须无权访问其他敏感表。否则,校验形同虚设。
相关文章
- hbase 可视化的成本究竟多高 07-29
- hbase 可视化存在哪些难点 07-29
- hbase 可视化的安全性怎样保障 07-29
- hbase 可视化的更新速度有多快 07-29
- hbase zookeeper 怎样处理节点加入 07-29
- hbase 数据抽取的效率如何提升 07-29