如何规避sql存储过程注入_严格清洗变量并使用预处理

SQL Server 存储过程中 EXEC 动态拼接字符串时为什么总被注入?

因为 EXEC(或 sp_executesql)执行的是纯字符串,变量一拼进去,就等于把用户输入直接喂给了 SQL 引擎。哪怕加了 REPLACE(@input, ””, ”””) 这种“手动转义”,也挡不住绕过——比如用注释符 — 截断后续校验逻辑,或用 CHAR(0x27) 绕过单引号检测。

真正有效的做法只有一条:不拼接,改用参数化。

所有外部输入必须作为参数传入 sp_executesql,不能出现在 SQL 字符串里EXEC(@sql) 形式一律禁用;sp_executesql 是唯一安全入口表名、列名等无法参数化的部分,只能白名单校验(如 CASE WHEN @col IN (‘name’, ’email’) THEN @col ELSE THROW 50000, ‘Invalid column’, 1 END)

如何用 sp_executesql 正确传递参数?

关键不是“有没有参数”,而是“参数是否在 SQL 字符串外定义、并在执行时绑定”。很多人写成这样:

DECLARE @sql NVARCHAR(MAX) = N’SELECT * FROM users WHERE id = ‘ + CAST(@id AS NVARCHAR(10)); EXEC sp_executesql @sql;

这仍是拼接,@id 已经被提前转成字符串塞进去了,毫无防护意义。

正确写法是:

DECLARE @sql NVARCHAR(MAX) = N’SELECT * FROM users WHERE id = @id AND status = @status’;<br>EXEC sp_executesql @sql,<br> N’@id INT, @status NVARCHAR(20)’,<br> @id = @id,<br> @status = @status;@sql 字符串里只出现参数占位符(@id, @status),不出现任何变量值第二个参数是参数签名,必须显式声明类型和长度(NVARCHAR(20) 不能简写为 NVARCHAR)第三个及之后的参数是实际值绑定,顺序/名称需与签名一致

哪些地方看似安全,其实仍可能中招?

白名单、长度限制、类型转换这些手段单独用,都扛不住构造精巧的攻击。

ISNUMERIC(@input) = 1 → 不拦得住 ‘1e3’ 或 ‘1,2,3’,更拦不住后面接 OR 1=1 –LEN(@input) <= 10 → 攻击载荷可以很短,比如 ‘; DROP TABLE users–用 QUOTENAME() 处理对象名 → 安全,但仅限于数据库对象(表、列、schema),不能用于值;且必须配合白名单,否则 QUOTENAME(‘users; DROP TABLE log–‘) 会变成 [users; DROP TABLE log–],依然执行失败但可能泄露结构

MySQL / PostgreSQL 存储过程是否同样适用?

不适用。SQL Server 的 sp_executesql 是特例,它原生支持参数绑定;而 MySQL 的 PREPARE … EXECUTE 虽然语法类似,但参数只支持标量值,不支持表名/列名,且错误处理弱;PostgreSQL 的 EXECUTE … USING 更接近,但动态语句中仍需用 format() 拼接对象名——这时必须用 quote_ident() 或 quote_literal(),且不能混用。

跨数据库时,最稳的底线是:值一律参数化,对象名一律白名单 + 显式校验函数(如 PostgreSQL 的 pg_get_userbyid() 配合 schema 查询)。

参数化不是加个 @ 就完事,是整条执行链路里,用户输入从进来到执行完,一次都不许变成字符串的一部分。这点容易被忽略,但恰恰是防御成败的分水岭。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。