【问题标题】:.Net & SQL Server 2005: preventing injection when querying against a list.Net & SQL Server 2005:查询列表时防止注入
【发布时间】:2015-08-21 08:56:36
【问题描述】:

我有一个搜索表单,需要查询另一个系统的 SQL Server 2005 数据库(因此我无法控制它们的存储过程/函数)。

通常为了防止注入,我的代码如下所示:

SqlCommand cmd = new SqlCommand("SELECT * FROM myTable WHERE Id = @Id", conn);
cmd.Parameters.AddWithValue("@Id", 1234);

这种情况有点不同,因为他们要求表单允许以逗号分隔的值列表也能正常工作。那么我该怎么做这样的事情:

string ids = "1234, 1235, 1236";
SqlCommand cmd = new SqlCommand("SELECT * FROM myTable WHERE Id IN (" + ids + ")", conn);

以一种安全的方式,一次查询?

【问题讨论】:

    标签: .net sql-server-2005 .net-4.0


    【解决方案1】:

    有很多方法可以传入列表并在 SQL 中进行类型检查。如果您使用存储过程而不是这种即席 SQL,您可以简单地将列表作为字符串参数传入并在过程中对其进行解析。如果列表有任何非整数,它将简单地出错。

    例如这个函数:

    CREATE FUNCTION [dbo].[SplitInts]
    (
       @List       VARCHAR(MAX),
       @Delimiter  CHAR(1)
    )
    RETURNS TABLE
    AS
       RETURN ( SELECT Item FROM ( SELECT Item = x.i.value('(./text())[1]', 'int') FROM 
                ( SELECT [XML] = CONVERT(XML, '<i>' + REPLACE(@List, @Delimiter, '</i><i>') 
                  + '</i>').query('.') ) AS a CROSS APPLY [XML].nodes('i') AS x(i)
              ) AS y WHERE Item IS NOT NULL
       );
    GO
    

    可以这样调用:

    SELECT Item FROM dbo.SplitInts('1234, 1235, 1236', ',');
    

    结果:

    Item
    ----
    1234
    1235
    1236
    

    如果你尝试使用任何字符串,它会爆炸:

    SELECT Item FROM dbo.SplitInts('1234; DROP TABLE bobbytables;', ',');
    

    结果:

    消息 245,级别 16,状态 1,第 1 行
    转换时转换失败 nvarchar 值 '1234; DROP TABLE bobbytables;'数据类型为 int。

    因此,使用该拆分函数,您的存储过程可以这样编写:

    CREATE PROCEDURE dbo.GetMyTable
        @List VARCHAR(MAX)
    AS
    BEGIN
        SET NOCOUNT ON;
    
        SELECT t.col1, t.col2 --, ...
          FROM dbo.myTable AS t
          INNER JOIN dbo.SplitInts(@List, ',') AS i
          ON t.Id = i.Item;
    END
    GO
    

    这可以使用强类型字符串参数 @List 从 .NET 调用,而无需担心 SQL 注入 - 只要您使用 StoredProcedure commandType 将参数传递给正确的调用并且不要尝试自己构建EXEC ... 字符串。

    在 SQL Server 2008 中,您可以更进一步并使用表值参数 (TVP),它可以让您将数据 填充到来自 DataTable 的存储过程参数中。

    【讨论】:

    • 嗨 Aaron - 从帖子来看,我的一个限制是我无法控制他们的存储过程或函数,否则我会做类似的事情
    • 好吧,希望其他人有一些聪明的东西,因为您不能只将参数作为字符串提供 - SQL Server 中没有数组。也许在 .NET 中,您可以遍历字符串并验证它们都是整数,然后再将它们重新组合在一起并构建查询。客户是否有兴趣防止注射?也许他们会更开放地允许您创建存储过程来防止它。
    • 我认为 String.Split (msdn.microsoft.com/en-us/library/b873y76a.aspx) 和 Int.TryParse (msdn.microsoft.com/en-us/library/f02979c7.aspx) 的简单组合可以在 .NET 中做到这一点。
    【解决方案2】:

    这是我能想到的最佳解决方案;如果有人发现任何漏洞,请告诉我:

    string idList = "1234, 1235, 1236";
    
    XElement ids = new XElement("ds");
    foreach (string s in idList.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(id => id.Trim()))
        ids.Add(new XElement("d", s));
    
    SqlCommand cmd = new SqlCommand("SELECT * FROM myTable WHERE Id IN (SELECT T.ids.value('.', 'int') FROM @Ids.nodes('/ds/d') AS T(ids))", conn);
    cmd.Parameters.Add(new SqlParameter("@Ids", ids.ToString(SaveOptions.DisableFormatting)) { SqlDbType = SqlDbType.Xml });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-16
      • 2017-07-24
      • 1970-01-01
      • 1970-01-01
      • 2010-09-08
      • 1970-01-01
      相关资源
      最近更新 更多