SQL Server堆叠注入成功的关键在于账号权限而非分号:即使输入含分号,若账号无DROP/EXEC等权限或驱动禁用多语句,攻击仍失败;真正防护是限制账号权限至最小必要范围,如仅授予特定表SELECT和指定存储过程EXECUTE权限,并显式DENY危险操作。

SQL Server 默认不支持堆叠查询注入,但一旦启用了 SET ANSI_NULLS OFF 或使用了某些特殊驱动/配置(如旧版 ODBC + SQLExecDirect 多语句模式),攻击者仍可能绕过限制执行多条语句。真正有效的防护不是靠“禁用分号”,而是切断执行链路——核心是让账号根本没权限执行危险操作。
SQL Server 堆叠注入为什么能成功?关键在账号权限
堆叠注入(如 123; DROP TABLE users;)能否执行,不取决于输入是否含分号,而取决于:数据库驱动是否允许多语句(如 SqlCommand 默认禁止,但 SqlConnection.ConnectionString 加上 MultipleActiveResultSets=true 并不等于开启堆叠)、以及当前数据库用户是否有 DROP、INSERT、EXEC 等权限。很多开发误以为“只要不用 exec sp_executesql 就安全”,其实只要账号有 db_owner 角色,哪怕只用 SELECT 语句开头,后面接分号+恶意语句,照样可能被执行。
- SQL Server 的
sa或db_owner用户可执行任意语句,包括堆叠中的CREATE、EXEC xp_cmdshell、SHUTDOWN - 普通应用账号若被授予
db_ddladmin或db_securityadmin,就足以创建临时表、改权限、甚至提权 - 即使只开
SELECT,若账号还能访问sys.dm_exec_sessions或master..xp_cmdshell(且该功能已启用),仍可能被用于信息探测或二次利用
如何创建一个真正受限的 SQL Server 用户
不能只靠 UI 勾选角色,必须用显式语句控制权限边界。以下是在 master 和目标数据库中分步操作:
第一步:创建登录(仅限指定 IP 或域,避免远程爆破)
CREATE LOGIN app_user WITH PASSWORD = 'StrongP@ssw0rd2026!', CHECK_EXPIRATION = ON, CHECK_POLICY = ON;
第二步:切换到业务数据库,创建用户并剥离所有默认角色
USE YourDB; CREATE USER app_user FOR LOGIN app_user; ALTER ROLE db_datareader DROP MEMBER app_user; ALTER ROLE db_datawriter DROP MEMBER app_user;
第三步:只授必要对象级权限(禁止通配符授权)
- 只读某张表:
GRANT SELECT ON dbo.orders TO app_user; - 只允许调用特定存储过程:
GRANT EXECUTE ON dbo.sp_get_order_status TO app_user; - 明确拒绝危险操作:
DENY ALTER ANY DATABASE TO app_user;、DENY CONTROL SERVER TO app_user;
⚠️ 注意:db_denydatareader 角色会覆盖 GRANT,慎用;优先用 DENY 显式封堵。
连接字符串和代码层必须同步收紧
即使数据库账号很干净,应用层仍可能因配置不当放大风险:
- 禁用
Allow User Variables=True(仅 MySQL 相关,SQL Server 不适用,但常被混淆) - 连接字符串中不要加
TrustServerCertificate=true(易受中间人劫持,间接导致凭据泄露) - .NET 中必须用
SqlCommand+ 参数化,禁用string.Format或$"SELECT ... WHERE id = {id}" - 如果业务真需要动态 SQL(极少数场景),必须用
sp_executesql+ 参数,且该存储过程本身要限定在只读 schema 下运行
常见错误是:账号设成只读,但代码里用 SqlCommand("DELETE FROM logs WHERE id IN (" + ids + ")") —— 这时分号根本不是问题,单引号闭合才是突破口。
验证权限是否真的生效
别信文档,要亲手测。用新账号登录后执行这几条:
-- 应成功 SELECT TOP 1 * FROM dbo.orders; <p>-- 应报错:“The SELECT permission was denied on the object 'orders'” SELECT * FROM dbo.users;</p><p>-- 应报错:“Cannot execute command because you do not have permission” DROP TABLE temp_test;</p><p>-- 应报错:“'xp_cmdshell' is not recognized as an internal or external command” EXEC xp_cmdshell 'whoami';
特别注意:SQL Server 错误消息默认会暴露表名、权限类型等信息。上线前务必在连接字符串中加 Application Name=ProdApp,并在 SQL Server 配置中关闭 show advanced options 和 user options 的详细错误返回,否则攻击者能靠报错反推权限结构。
最易被忽略的一点:权限继承。如果账号属于 public 角色以外的任何固定数据库角色(哪怕只是 db_backupoperator),都可能获得未预期的执行能力。每次新建账号后,运行 SELECT role FROM sys.database_role_members rm JOIN sys.database_principals rp ON rm.role_principal_id = rp.principal_id WHERE rm.member_principal_id = USER_ID('app_user'); 确认角色列表为空或严格可控。

















