存储过程内拼接字符串执行SQL与应用层同样危险,因SQL Server只执行最终语句;EXEC纯文本执行无校验,sp_executesql需显式声明参数类型并单独传值,表名列名等结构元素必须白名单校验。

存储过程里拼接字符串执行 SQL,和应用层拼接一样危险——SQL Server 不关心语句从哪来,只认最终执行的字符串结构。
EXEC 执行拼接字符串等于裸奔
只要用 EXEC(@sql) 执行动态拼出来的字符串,就失去所有参数隔离能力。哪怕你对输入做了 REPLACE(@input, '''', ''''''),攻击者仍能绕过,比如传入 users; DROP TABLE logs; --,EXEC 会直接当三条语句执行。
-
EXEC完全不解析参数,纯文本解释执行,连语法校验都跳过 - 错误消息可能泄露表结构(如
Invalid object name 'xxx'),帮攻击者探库 - 执行计划无法复用,每次都是新编译,性能差还容易被缓存污染
sp_executesql 不是加了就安全,关键看怎么用
sp_executesql 是唯一靠谱的替代方案,但很多人只写对一半:把参数名硬塞进 SQL 字符串里,或者漏写类型声明。
- 必须显式声明参数类型和长度:
N'@name NVARCHAR(50)',不能只写N'@name' - 参数值要单独传,不能拼进 SQL 字符串:
@name = @name,不是'@name = ''' + @name + '''' - 字符串里只留占位符:
N'SELECT * FROM users WHERE name = @name',别手动加引号 - 如果传入的是
INT类型变量,但调用时却传字符串'1 OR 1=1',SQL Server 可能隐式转成1——这不是安全,是掩盖上游传参失控
表名、列名、ORDER BY 字段这些根本不能参数化
SQL Server 不允许在 WHERE 或 FROM 子句中用 @table_name 占位,所以必须拼。但拼之前得确认它真合法。
- 别信
QUOTENAME(@table_name)能兜底——它只防单引号,防不了]; DROP TABLE x; -- - 必须白名单校验:
IF @sort_col NOT IN ('created_at', 'status') RAISERROR(...) - 更稳妥的是查系统视图:
SELECT 1 FROM sys.tables WHERE name = @table_name AND schema_id = SCHEMA_ID('dbo') - 动态
ORDER BY必须套QUOTENAME(),且字段名必须来自白名单映射,不能直接用用户输入
真正难的不是写对 sp_executesql 的语法,而是分清哪些东西“能走参数”、哪些“必须拼但得先验”,以及验完之后要不要再加一层权限控制。很多漏洞爆发,不是因为不会用 sp_executesql,而是把不该拼的拼了,或把该验的漏验了。










