首页 > 数据库 >SQL存储过程参数安全传递防止注入攻击

SQL存储过程参数安全传递防止注入攻击

来源:互联网 2026-07-09 12:34:40

存储过程中参数必须直接引用,不能拼接至动态SQL字符串。需用参数化绑定(如USING、sp_executesql)传递值。动态对象名必须经白名单或系统视图校验,不能仅靠转义函数。执行账号权限需严格限制,防止越权访问。

在SQL存储过程中传递参数以防止注入攻击时,必须遵循若干关键原则。其中最为根本的一条是:参数应当直接引用,绝不能拼接到动态SQL字符串中——这是安全底线。

SQL存储过程参数安全传递防止注入攻击

长期稳定更新的攒劲资源: >>>点此立即查看<<<

存储过程参数必须直接使用,不能拼入动态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_namep_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,但无法区分不存在和恶意截断

最容易被忽略的是权限上下文:即使表名校验通过,执行账号也必须无权访问其他敏感表。否则,校验形同虚设。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备2026025700号-3 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。