存储过程不防sql注入,关键在编写方式;禁用字符串拼接执行动态sql,所有外部输入须白名单校验或参数化绑定,参数需强类型声明且全程不参与拼接。

存储过程本身不防SQL注入,关键看你怎么写——只要在内部拼接字符串再执行,哪怕包着 CREATE PROCEDURE 外壳也等于裸奔。
别在存储过程里用 EXEC(@sql) 或 sp_executesql 拼接用户输入
这是最常见、最致命的坑。很多人以为“用了存储过程就安全了”,结果在过程体里写:SET @sql = 'SELECT * FROM ' + @table_name + ' WHERE id = ' + @id,再 EXEC(@sql),等于把攻击面直接暴露给数据库引擎。
- 所有外部输入(包括表名、列名、排序字段)都不能靠字符串拼接进 SQL 文本
-
sp_executesql安全的前提是:SQL 字符串为硬编码或白名单控制,参数全部走@param占位符传递 - 如果非得动态表名,必须白名单校验:
IF @table_name NOT IN ('orders', 'users') THROW 50000, 'Invalid table name', 1
所有参数必须声明强类型,且全程不参与字符串拼接
参数化不是靠调用层传参就完事,存储过程内部要用好这些参数——SQL Server 会把 @status 当纯数据绑定,不会进入语法解析阶段。
- 声明时明确长度和类型,比如
@status NVARCHAR(20),避免用MAX或UNICODE模糊类型放大风险 - WHERE 条件直接写
WHERE status = @status,而不是WHERE status = ''' + @status + ''' - 数值型参数优先用
INT、BIGINT,避免用字符串接收后再转换
警惕 INFORMATION_SCHEMA 和动态标识符场景
查询系统视图时若带用户输入(如按表名查字段),极易被绕过。quote_ident 类函数在 T-SQL 中不存在,SQL Server 没有等效内置函数,不能依赖“转义”来保命。
- 不要写
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @user_input - 必须走白名单:
IF @table_name IN (SELECT name FROM sys.tables) BEGIN ... END - 动态
ORDER BY字段?只允许预设值:CASE @sort_col WHEN 'id' THEN id WHEN 'name' THEN name END
真正难的不是写对一个存储过程,而是确保整个调用链——从 Web 层传参、到中间件处理、再到存储过程内部——没有一处把用户输入当代码使。很多上线后被扫出漏洞的案例,问题不在存储过程本身,而在应用层把恶意字符串原样塞进了 @param。











