Rav*_*avi 18 sql-server-2008 security sql-server sql-injection dynamic-sql
我做了以下存储过程:
ALTER PROCEDURE usp_actorBirthdays (@nameString nvarchar(100), @actorgender nvarchar(100))
AS
SELECT ActorDOB, ActorName FROM tblActor
WHERE ActorName LIKE '%' + @nameString + '%'
AND ActorGender = @actorgender
Run Code Online (Sandbox Code Playgroud)
现在,我尝试做这样的事情。也许我做错了,但我想确保这样的过程可以防止任何 SQL 注入:
EXEC usp_actorBirthdays 'Tom', 'Male; DROP TABLE tblActor'
Run Code Online (Sandbox Code Playgroud)
下图显示了在 SSMS 中执行的上述 SQL 并且结果显示正确而不是错误:
顺便说一句,我在查询完成后在分号后面添加了那部分。然后我再次执行它,但是当我检查表 tblActor 是否存在时,它仍然存在。难道我做错了什么?或者这真的是防注射的吗?我想我在这里也想问的是,这是一个像这样安全的存储过程吗?谢谢你。
Sol*_*zky 38
此代码工作正常,因为它是:
为了使 SQL 注入起作用,您必须构建一个查询字符串(您没有这样做)并且不能将单个撇号 ( '
) 转换为转义的撇号 ( ''
)(这些是通过输入参数转义的)。
在您尝试传递“妥协”值时,该'Male; DROP TABLE tblActor'
字符串就是一个普通的字符串。
现在,如果你正在做一些事情:
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT fields FROM table WHERE field23 = '
+ @InputParam;
EXEC(@SQL);
Run Code Online (Sandbox Code Playgroud)
那么这很容易受到 SQL 注入的影响,因为该查询不在当前的、预先解析的上下文中;该查询目前只是另一个字符串。因此,的值@InputParam
可能是'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
并且可能会出现问题,因为该查询将被呈现和执行,如下所示:
SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
Run Code Online (Sandbox Code Playgroud)
这是使用存储过程的(几个)主要原因之一:本质上更安全(好吧,只要您不通过构建像我上面展示的那样的查询而不验证所使用的任何参数的值来规避该安全性)。虽然如果您需要构建动态 SQL,首选方法是使用sp_executesql
以下参数对其进行参数化:
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT fields FROM table WHERE field23 = @SomeDate_tmp';
EXEC sp_executesql
@SQL,
N'SomeDate_tmp DATETIME',
@SomeDate_tmp = @InputParam;
Run Code Online (Sandbox Code Playgroud)
使用这种方法,试图传入'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
一个DATETIME
输入参数的人在执行存储过程时会得到一个错误。或者即使存储过程接受@InputParameter
as NVARCHAR(100)
,它也必须转换为 aDATETIME
才能传递给该sp_executesql
调用。即使动态 SQL 中的参数是字符串类型,首先进入存储过程的任何单个撇号都会自动转义为双撇号。
有一种鲜为人知的攻击类型,攻击者试图用撇号填充输入字段,这样存储过程中将用于构造动态 SQL 但声明太小的字符串不能容纳所有内容并推出结尾的撇号,并以某种方式以正确数量的撇号结束,以便不再在字符串中“转义”。这称为 SQL 截断,在 MSDN 杂志的一篇题为“新的 SQL 截断攻击以及如何避免它们”的文章中进行了讨论,该文章由 Bala Neerumalla 撰写,但该文章已不再在线。包含本文的问题 — MSDN 杂志 2006 年 11 月版— 仅可作为 Windows 帮助文件(在.chm格式)。如果您下载它,由于默认安全设置,它可能无法打开。如果发生这种情况,请右键单击MSDNMagazineNovember2006en-us.chm文件并选择“属性”。在这些选项卡之一中,将有一个选项“信任这种类型的文件”(或类似的东西)需要检查/启用。单击“确定”按钮,然后再次尝试打开.chm文件。
截断攻击的另一个变体是,假设一个局部变量用于存储“安全”用户提供的值,因为它有任何单引号加倍以进行转义,以填充该局部变量并放置单引号在末尾。这里的想法是,如果局部变量的大小不正确,则在第二个单引号的末尾将没有足够的空间,让变量以单个单引号结尾,然后与单引号组合结束动态 SQL 中的文字值,将该结束单引号转换为嵌入的转义单引号,然后动态 SQL 中的字符串文字以下一个旨在开始下一个字符串文字的单引号结束。例如:
-- Parameters:
DECLARE @UserID INT = 37,
@NewPassword NVARCHAR(15) = N'Any Value ....''',
@OldPassword NVARCHAR(15) = N';Injected SQL--';
-- Stored Proc:
DECLARE @SQL NVARCHAR(MAX),
@NewPassword_fixed NVARCHAR(15) = REPLACE(@NewPassword, N'''', N''''''),
@OldPassword_fixed NVARCHAR(15) = REPLACE(@OldPassword, N'''', N'''''');
SELECT @NewPassword AS [@NewPassword],
REPLACE(@NewPassword, N'''', N'''''') AS [REPLACE output],
@NewPassword_fixed AS [@NewPassword_fixed];
/*
@NewPassword REPLACE output @NewPassword_fixed
Any Value ....' Any Value ....'' Any Value ....'
*/
SELECT @OldPassword AS [@OldPassword],
REPLACE(@OldPassword, N'''', N'''''') AS [REPLACE output],
@OldPassword_fixed AS [@OldPassword_fixed];
/*
@OldPassword REPLACE output @OldPassword_fixed
;Injected SQL-- ;Injected SQL-- ;Injected SQL--
*/
SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
+ @NewPassword_fixed + N''' WHERE [TableNameID] = '
+ CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
+ @OldPassword_fixed + N''';';
SELECT @SQL AS [Injected];
Run Code Online (Sandbox Code Playgroud)
这里,要执行的动态 SQL 现在是:
UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';
Run Code Online (Sandbox Code Playgroud)
同样的动态 SQL,以更易读的格式,是:
UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';
Injected SQL--';
Run Code Online (Sandbox Code Playgroud)
解决这个问题很容易。只需执行以下操作之一:
不要使用局部变量来存储“固定”值;只是把REPLACE()
直接进入创建动态SQL:
SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
+ REPLACE(@NewPassword, N'''', N'''''') + N''' WHERE [TableNameID] = '
+ CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
+ REPLACE(@OldPassword, N'''', N'''''') + N''';';
SELECT @SQL AS [No SQL Injection here];
Run Code Online (Sandbox Code Playgroud)
动态 SQL 不再受到损害:
UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';
Run Code Online (Sandbox Code Playgroud)关于上述截断示例的注意事项:
DELETE tableName
破坏性,但不太可能添加后门用户或更改管理员密码。有关 SQL 注入的更多详细信息(涵盖各种 RDBMS 和场景),请参阅开放 Web 应用程序安全项目(OWASP) 中的以下内容:
SQL 注入测试
有关 SQL 注入和 SQL 截断的相关堆栈溢出答案:
替换 ' 转义字符后 T-SQL 的安全性如何?