防 SQL 注入的基本做法
防 SQL 注入的四道核心防线:参数化查询、预编译、最小权限、输入校验,以及在 T-SQL 里写动态 SQL 时的安全写法。
结论先行
防注入的核心就一条:永远不要把用户输入直接拼进 SQL 文本。四道防线按优先级排:①参数化/预编译(根治)→ ②最小权限账号(限损)→ ③输入校验(纵深)→ ④动态 SQL 时参数化批处理。做到 ①② 两项,注入面就基本关死了。
四道防线
① 参数化查询(根治手段)
-- 错误:拼接
DECLARE @sql NVARCHAR(400) = N'DELETE FROM Orders WHERE Id = ' + CAST(@Input AS NVARCHAR(10));
EXEC(@sql);
-- 正确:参数化批处理
EXEC sp_executesql N'DELETE FROM Orders WHERE Id = @Id', N'@Id int', @Id = @Input;
应用层同理:所有 ORM 的参数绑定、ADO 的 @参数 占位符都是参数化;字符串拼 SQL 本身就是病。
② 最小权限(限损)
应用账号只给需要的表的 SELECT/INSERT/UPDATE 权限,不给 sysadmin、不给 DROP 权——即使被注入执行了语句,能破坏的范围也有限(见最小权限相关条目)。
③ 输入校验(纵深)
白名单优先:期望数字就只收数字,期望枚举就收枚举值;校验在入口做,别指望数据库替你筛。
④ 动态 SQL 的额外注意
EXEC(@动态语句) 必须走 sp_executesql 参数化;语句里要拼入表名/列名(不能参数化的对象名)时,只能白名单映射(从代码里已知的合法名列表里选),绝不接受用户直传。
常见误判
「我做了输入过滤(禁分号、禁 DROP)」≠ 防注入。过滤永远比参数化晚到一步,绕过成本极低。
- 报错信息泄露结构(列名、表名):面向用户层的错误不要原样透出数据库报错(应用层处理)。
- 视图/存储过程里
EXECUTE AS权限提升:定义权限的存储过程是「白盒」,改对象逻辑前评估调用方权限。
在 SQL Server 2022 Developer 条目环境下可用 SSMS 管理工具条目 演练参数化写法与权限配置;旧引擎(如 SQL Server 2019 Developer)上这些防线的要求完全相同——注入是应用与权限设计问题,不随引擎版本变。