1

在从经典的 ASP 防止 SQL 注入保护中获得了防止 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 注入的攻击。

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

在我看来,虽然这种方法可以保证安全,但它需要我在数据库中的每次更改时修改静态列表(尽管很少见)。

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

4

4 回答 4

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 查询仅使用表中实际存在的列名生成。(任何废话都会被忽略。)

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

于 2012-06-13T19:22:29.403 回答
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 的建议验证每个元素。然后使用所有经过验证的参数来构建查询字符串,并在第二个查询中将名称作为参数传递。

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

于 2012-06-13T20:06:06.007 回答
0

我在家,没有要测试的数据库,但应该这样做基本上,从数据库中获取所有适合 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
于 2012-06-13T19:33:22.933 回答
-1

看看http://www.userfriendlythinking.com/Blog/BlogDetail.asp?p1=7013&p2=119&p7=3001

它显示了参数化查询的用法。

于 2012-06-14T11:06:35.170 回答