sp_executesql是SQL Server中唯一支持真正参数绑定的机制,可天然隔离数据与代码;表名、列名等标识符须通过白名单或系统视图校验,不可仅依赖QUOTENAME();WHERE条件应使用IS NULL逻辑替代字符串拼接,SqlParameter必须显式指定类型并正确处理空值。

sp_executesql 必须代替 EXEC
SQL Server 中,EXEC(@sql) 是注入高危路径——它把整个字符串当命令执行,参数值一旦混入,立刻被解析为语句。而 sp_executesql 才是唯一支持真正参数绑定的机制,值不参与语法解析,天然隔离数据与代码。
常见错误是以为加了 QUOTENAME() 就安全,比如:EXEC('SELECT * FROM ' + QUOTENAME(@table_name) + ' WHERE id = ' + @id)。这里 @id 仍被拼进字符串,攻击者传入 1 OR 1=1 就直接生效。
-
@sql字符串里只能出现占位符(如@p_id),不能出现任何用户输入的字面量 - 类型声明必须显式、精确,例如
N'@p_id int',不能写成N'@p_id nvarchar(10)'去传整数,否则可能触发隐式转换失败 - 不要用
REPLACE(@input, '''', '''''')手动转义——绕过手段太多,且对 Unicode 同形字、嵌套注释无效
表名/列名等标识符必须白名单或系统视图校验
sp_executesql 只能绑定值,无法绑定表名、列名、排序方向这些语法结构。所以它们必须提前验证合法性,不能靠 QUOTENAME() 单独兜底。
错误做法:SET @sql = 'SELECT * FROM ' + QUOTENAME(@table_name) —— QUOTENAME() 对 'users; DROP TABLE logs--' 这种输入会生成 [users; DROP TABLE logs--],仍是合法标识符,后续执行照样出事。
- 优先查
sys.tables或sys.columns:例如IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE name = @table_name AND schema_id = SCHEMA_ID('dbo')) RAISERROR(...) - 避免只用
OBJECT_ID(@table_name)判断——它不校验 schema,'malicious;--'可能被截断后误判存在 - 排序字段这类有限枚举场景,直接白名单
CASE WHEN @sort_col IN ('created_at', 'status') THEN @sort_col ELSE 'id' END
WHERE 条件别拼字符串,用 IS NULL 判断替代
动态拼 AND status = ''' + @status + '''' 是典型翻车点:空值判断漏掉、类型转换异常、执行计划无法复用,三者叠加等于敞开注入口。
静态 SQL + 条件分支更稳:WHERE (@status IS NULL OR status = @status)。SQL Server 优化器能识别这种模式并生成合理计划,且完全规避字符串拼接。
- 注意
@status = ''和@status IS NULL语义不同,前端传空字符串时需按业务显式处理 - 若真要跳过某些条件(比如全文索引场景),改用
OPTION (RECOMPILE)让每次编译适配实际参数,而不是拼字符串 - 禁止在存储过程里用
CONTEXT_INFO或临时表存用户输入后再拼 SQL——这些是隐蔽的拼接点,常被忽略
SqlParameter 传参必须显式指定类型
调用层的 SqlParameter 是第一道防线。用 AddWithValue() 看似省事,但类型推断常导致隐式转换失败、执行计划错乱,甚至让存储过程参数定义失效。
例如存储过程定义 @id int,你传字符串 "123",AddWithValue 推成 nvarchar(3),SQL Server 可能拒绝隐式转 int 或走错索引路径。
- 明确指定类型:
cmd.Parameters.Add("@id", SqlDbType.Int).Value = userId - 空值必须赋
DBNull.Value,别依赖自动推断 - EF Core 调用存储过程时,
FromSqlRaw默认不参数化,即使写了@p0占位符,没传parameters对象就等于裸奔
真实风险往往藏在“看起来已参数化”的地方:一个未校验的表名变量、一次隐式类型转换、一条被忽略的 CONTEXT_INFO 使用,都可能让整套防护失效。防注入不是加一层函数调用,而是确认每个用户可控输入在进入 SQL 解析器前,已被严格归类为“值”或“结构”,并走对应路径处理。

















