为什么在存储过程中的此查询上不发生SQL注入?


18

我做了以下存储过程:

ALTER PROCEDURE usp_actorBirthdays (@nameString nvarchar(100), @actorgender nvarchar(100))
AS
SELECT ActorDOB, ActorName FROM tblActor
WHERE ActorName LIKE '%' + @nameString + '%'
AND ActorGender = @actorgender

现在,我尝试做这样的事情。也许我做错了,但是我想确保这样的过程可以防止任何SQL注入:

EXEC usp_actorBirthdays 'Tom', 'Male; DROP TABLE tblActor'

下图显示了以上在SSMS中执行的SQL,结果正确显示,而不是错误:

在此处输入图片说明

顺便说一句,我在查询完成执行后在分号后添加了该部分。然后我再次执行它,但是当我检查表tblActor是否存在时,它仍然存在。难道我做错了什么?还是这真的防注射?我想我也想问的是这样的存储过程是否安全?谢谢。


您是否尝试过此EXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'
MarmiK

Answers:


38

该代码可以正常工作,因为它是:

  1. 参数化
  2. 没有做任何动态SQL

为了使SQL注入能够正常工作,您必须构建一个查询字符串(不执行此操作),并且将单个撇号(')转换为转义的撇号('')(通过输入参数对它们进行转义)。

在您尝试传递“妥协”值时,'Male; DROP TABLE tblActor'字符串就是这样,即纯字符串。

现在,如果您正在执行以下操作:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT fields FROM table WHERE field23 = '
          + @InputParam;

EXEC(@SQL);

然后由于查询不在当前的预先解析的上下文中,因此容易受到SQL注入的影响;该查询目前只是另一个字符串。因此,of的值@InputParam可能为'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;并且可能会出现问题,因为该查询将按以下方式呈现和执行:

SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;

这是使用存储过程的(几个)主要原因之一:本质上更安全(只要您不通过构建如上所示的查询而不验证所使用的任何参数的值来规避安全性)。虽然如果您需要构建Dynamic 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;

使用这种方法,有人试图在传递'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;一个DATETIME执行存储过程时,输入参数会得到一个错误。或者,即使存储过程接受@InputParameterNVARCHAR(100),它也必须转换为DATETIME才能传递给该sp_executesql调用。并且即使Dynamic SQL中的参数是字符串类型,首先进入存储过程的任何单个撇号也会自动转义为双撇号。

有一种鲜为人知的攻击类型,其中,攻击者尝试用撇号填充输入字段,以使存储过程内部的一个字符串(该字符串将用于构造动态SQL,但声明为太小)无法容纳所有内容并推出结尾的撇号,并以某种正确的撇号结束,从而不再在字符串中“转义”。这称为SQL截断,在Bala Neerumalla的MSDN杂志文章“新的SQL截断攻击和如何避免它们”中进行了讨论,但该文章不再在线。包含本文的问题(2006年11月版的MSDN Magazine)仅作为Windows帮助文件提供(在.chm中格式)。如果下载它,由于默认的安全设置,它可能无法打开。如果发生这种情况,请右键单击MSDNMagazineNovember2006en-us.chm文件,然后选择“属性”。在这些标签中的一个中,将有一个“信任此类型的文件”(或类似名称)选项,需要选中/启用。单击“确定”按钮,然后尝试再次打开.chm文件。

截断攻击的另一个变体是,假设使用局部变量来存储用户提供的“安全”值,因为它会将任何单引号加倍以进行转义,以便逃脱,以填充该局部变量并放置单引号在末尾。这里的想法是,如果局部变量的大小不正确,则第二个单引号的结尾将没有足够的空间,请将该变量以单个单引号结尾,然后将其与结束Dynamic SQL中的文字值,将结束的单引号转换为嵌入式转义的单引号,然后Dynamic 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];

现在,要执行的动态SQL现在是:

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

更具可读性的相同的Dynamic SQL是:

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';

Injected SQL--';

解决这个问题很容易。只需执行以下操作之一:

  1. 除非绝对必要,否则不要使用动态SQL !(我将其列为第一,因为它实际上应该是首先要考虑的)。
  2. 适当调整局部变量的大小(即,应为输入参数大小的两倍,以防万一传入的所有字符都是单引号。
  3. 不要使用局部变量来存储“固定”值;只需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];

    动态SQL不再受到威胁:

    UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

有关上述截断示例的注意事项:

  1. 是的,这是一个非常人为的例子。仅用15个字符即可插入的内容很少。当然,这可能具有DELETE tableName破坏性,但不太可能添加后门用户或更改管理员密码。
  2. 这种类型的攻击可能需要了解代码,表名等信息。不太可能由随机的陌生人/脚本小子来完成,但是我确实在一个受到一个相当不高兴的前员工了解的漏洞的地方工作在一个没有其他人知道的特定网页中。这意味着,有时攻击者确实对系统有深入的了解。
  3. 当然,重置每个人的密码很可能会受到调查,这可能会提示公司发生了攻击,但它仍可能提供足够的时间来注入后门用户,或者获取一些辅助信息以供以后使用/利用。
  4. 即使这种情况大部分是学术性的(即在现实世界中不太可能发生),但这仍然不是没有可能。

有关与SQL注入相关的更多详细信息(涵盖各种RDBMS和方案),请参阅Open Web Application Security Project(OWASP)中的以下内容:
测试SQL注入

有关SQL注入和SQL截断的相关堆栈溢出答案:
替换'转义符后,T-SQL的安全性如何?


2
哦,非常感谢,这是一个很好的答案。我现在知道了。我真的很想看看您在结尾处提到的技术,如果您能找到的话,攻击者会尝试用撇号填充输入字段。提前致谢。我将保持打开状态,以防万一您找不到它,我将选择它作为答案。
拉维

1
@Ravi我找到了链接,但由于它们现在都已存档,因此它不再指向该文章。但是我添加了一些信息和有用的链接,但我仍在尝试在这些存档中找到该文章。
所罗门·鲁兹基

1
谢谢srutzsky,我将阅读OWASP文章和注射测试。如果我没记错的话,易受攻击的Web应用程序“ mutillidae”用于安全测试,我在大学里用“ OR 1 = 1”字符串进行了SQL注入,这在mutillidae中导致我以管理员身份登录Web应用程序。认为。那是我第一次接触SQL注入的时候。
拉维2015年

1
我也无法查看.chm文件,但是感谢您提供完美的答案以及所有有用的链接,包括stackoverflow的链接和OWASP的链接。我今天读了此书,并从中学到了很多。
拉维

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.