ASP子句 - SELECT子句中的SQL注入保护

时间:2012-06-13 19:06:33

标签: tsql asp-classic sql-server-2000 sql-injection

在获得classic ASP protection against SQL injection的SQL注入安全性方面得到了很大帮助之后,我遇到了一个主要问题,无法使用参数化查询解决。

name = Trim(Request.QueryString("name"))
flds = Trim(Request.QueryString("flds"))
sql = "set rowcount 0 select " & flds & " from [TABLE] where Name = '" & name & "'"

据我所知,参数化查询将防止WHERE子句中的SQL注入(在本例中为name字段。

flds是用户想要返回的以逗号分隔的参数列表。很明显,非常容易受到SQL注入攻击。

我必须保护代码的一个想法是使用静态生成的有效字段的dict,将flds字符串拆分为“,”,根据dict验证每个值,并构造SQL查询它将包含字典中存在的所有字段。

在我看来,虽然这种方法可以用于安全性,但它需要我在数据库中的每次更改时修改静态列表(无论这种情况很少见)。

是否有更好/正确的方法来保护此代码免受SQL注入攻击?

4 个答案:

答案 0 :(得分:2)

在SQL Server中创建一个拆分函数(对于较新的版本,有更好的版本,但这是你在SQL Server 2000中得到的):

CREATE FUNCTION dbo.SplitStrings
(
   @List       NVARCHAR(4000),
   @Delimiter  CHAR(1)
)
RETURNS @Items TABLE
(
   Item NVARCHAR(4000)
)
AS
BEGIN
   DECLARE
       @Item VARCHAR(12),
       @Pos  INT;

   WHILE LEN(@List)>0
   BEGIN
       SET @Pos = CHARINDEX(@Delimiter, @List);

       IF @Pos = 0
           SET @Pos = LEN(@List)+1;

       SET @Item = LEFT(@List, @Pos-1);

       INSERT @Items SELECT LTRIM(RTRIM(@Item));

       SET @List = SUBSTRING(@List, @Pos + LEN(@Delimiter), LEN(@List));

       IF LEN(@List) = 0 BREAK;
   END
   RETURN;
END
GO

然后创建一个存储过程:

CREATE PROCEDURE dbo.RunScaryQuery
  @columns NVARCHAR(4000),
  @table   NVARCHAR(255)
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @collist NVARCHAR(4000), @sql NVARCHAR(4000);

  SELECT @collist = COALESCE(@collist + ',', '') + c.name 
    FROM syscolumns AS c
    INNER JOIN dbo.SplitStrings(@columns, ',') AS s
    ON s.Item = c.name
    WHERE c.id = OBJECT_ID(@table);

  SELECT @sql = 'SELECT ' + @collist + ' FROM ' + @table
  -- where ...
  ;

  EXEC sp_executesql @sql;
END
GO

现在使用正确参数化的命令对象从ASP调用该存储过程。

这将确保仅使用表中实际存在的列名生成SQL查询。 (任何废话都会被忽略。)

这假设您将在列表中至少获得一个有效的列名称。

答案 1 :(得分:0)

我在家,没有数据库可以测试但是应该这样做 基本上,从db中获取适合where的所有字段,在数组中获取请求的字段并比较两个列表,仅输出所请求的字段。

name = Trim(Request.QueryString("name"))
flds = split(Trim(Request.QueryString("flds")),",")
sql = "set rowcount 0 select * from [TABLE] where Name = '" & name & "'"
set oRst = oConn.execute(sql)
on error resume next
do while not oRst.eof
  result    = ""
  separator = ""
  for each field in flds
    for each requested_field in flds
      if uCase(field.name) = uCase(trim(requested_field)) then
        result = result & separator & field.value
        separator = ","
      end if
    next
  next
  response.write result & "<br>"
  oRst.movenext
loop

答案 2 :(得分:0)

嗯......所以我会选择其他解决方案。

我首先有一个返回所有有效字段的SQL查询

select
   tcol.name
from
  sysObjects tobj
  join syscolumns tcol on tobj.id = tcol.id
where
  tobj.xtype = 'U'
  and tobj.name = '[TABLE]'

然后我按照@peter的建议验证每个元素。然后使用所有经过验证的参数构建查询字符串,并在第二个查询中将名称作为参数传递。

这似乎可以最大限度地减少数据库的开销和压力。

答案 3 :(得分:-1)