从SQL注入使SQL Server存储过程安全

时间:2011-12-19 19:43:36

标签: sql-server sql-server-2008 tsql

这可以很容易地在这里注入,因为@ID param通过输入它几乎可以在这个SQL语句中的任何内容,但是,你如何防止这种利用?

我更愿意在此级别而不是应用程序级别,任何建议中专门防止此漏洞利用?

CREATE PROCEDURE [dbo].[GetDataByID]
@ID bigint,
@Table varchar(150)
AS
BEGIN

Declare @SQL Varchar(1000)

SELECT @SQL = 'SELECT * FROM ' + @Table + ' WHERE ID = ' + CONVERT(varchar,@ID)

SET NOCOUNT ON;

EXEC(@sql)  
END

4 个答案:

答案 0 :(得分:9)

检查this page,它有一个很棒的动态sql指南,以及安全执行它们的选项

在你的情况下它应该是这样的:

SELECT @SQL =  N'SELECT * FROM ' + quotename(@Table) + N' WHERE ID = @xid' 
EXEC sp_executesql @SQL, N'@xid bigint', @ID

答案 1 :(得分:1)

1)创建一个具有标识PK并包含字符串表名称的新表 2)在您的程序中插入所有/仅有效的表格 3)使用此int identity PK作为存储过程的输入参数值(TableID) 4)在过程中,只需从给定的身份PK中查找字符串值(表名),您就可以安全地连接查询中的查找字符串。 5)你的WHERE子句没有问题,因为你传入了一个int

答案 2 :(得分:0)

我建议完全避免使用动态sql。问题如下:

  • 明显的注入附加方案
  • 二进制注入攻击更加智能,可以绕过传统的字符串转义
  • 性能是最重要的 - SQL Server旨在管理存储过程的执行计划,它们的运行速度比动态构建的查询要快。如果您使用的是动态sql,则根本不使用存储过程。如果您希望从多个表中选择代码的灵活性,您应该考虑使用ORM或其他东西来简化代码。考虑到你必须动态地传递表格,那么我甚至会说上面的程序没有意义,不同的解决方案是最好的选择。如果您只是针对SQL编写(即没有ORM),那么生成单独的proc的代码甚至会是更好的选择。

注意 QUOTENAME不会保证您注射安全。截断注射仍然是可能的。使用前请先阅读http://msdn.microsoft.com/en-us/library/ms161953.aspx

答案 3 :(得分:0)

虽然我一般会建议动态sql,但在这种情况下,我认为你可以通过检查@Table变量是否包含有效的表名来逃避它。

  • 问题是,如果您计划允许模式名称和/或跨数​​据库查询,我假设您不想离开数据库(或服务器),但允许不同的模式(AdventureWorks显示)如何使用它们)
  • 您可能还希望包含@Table的视图。
  • 如果您还检查了找到的对象是否实际上有ID列并且如果没有则抛出“用户友好”错误,那么它可能会“很好”。虽然是可选的。

只是在@table周围放置QuoteName()不会保护你免受一切。虽然功能很强,但它还远非完美。恕我直言,你最好的办法是解析@Table变量,检查其内容是否有效,然后根据获得的部分创建动态sql。 我开始做上面的大部分工作,令人惊讶的是它需要很多检查看起来像这样简单的东西=)

CREATE PROCEDURE [dbo].[GetDataByID] ( 
                                        @ID bigint,
                                        @Table nvarchar(300)
                                      )
AS

DECLARE @sql nvarchar(max)

DECLARE @server_name sysname,
        @db_name     sysname,
        @schema_name sysname,
        @object_name sysname,
        @schema_id   int        

SELECT @server_name = ParseName(@Table, 4),
       @db_name     = ParseName(@Table, 3),
       @schema_name = ParseName(@Table, 2),
       @object_name = ParseName(@Table, 1)

IF ISNULL(@server_name, @@SERVERNAME) <> @@SERVERNAME
    BEGIN
        RaisError('Queries are restricted to this server only.', 16, 1)
        Return(-1)
    END

IF ISNULL(@db_name, DB_Name()) <> DB_Name()
    BEGIN
        RaisError('Queries are restricted to this database only.', 16, 1)
        Return(-1)
    END


IF @schema_name IS NULL
    BEGIN
        IF NOT EXISTS ( SELECT *
                          FROM sys.objects
                         WHERE name = @object_name
                           AND type IN ('U', 'V') )
            BEGIN
                RaisError('Requested @Table not found. [%s]', 16, 1, @object_name)
                Return(-1)
            END

        SELECT @sql = 'SELECT * FROM ' + QuoteName(@object_name) + ' WHERE ID = @ID'
    END
ELSE
    BEGIN

        SELECT @schema_id = Schema_id(@schema_name)

        IF @schema_id IS NULL 
            BEGIN
                RaisError('Unrecognized schema requested [%s].', 16, 1, @schema_name)
                Return(-1)
            END

        IF NOT EXISTS ( SELECT *
                          FROM sys.objects
                         WHERE name = @object_name
                           AND schema_id = @schema_id
                           AND type IN ('U', 'V') )
            BEGIN
                RaisError('Requested @Table not found. [%s].[%s]', 16, 1, @schema_name, @object_name)
                Return(-1)
            END

        SELECT @sql = 'SELECT * FROM ' + QuoteName(@schema_name) + '.' + QuoteName(@object_name) + ' WHERE ID = @ID'
    END

EXEC sp_executesql @stmt   = @sql,
                   @params = N'@ID bigint',
                   @ID     = @ID

Return(0)       

Supra编译,但你可能需要解决一些错误,因为我没有完全检查所有代码路径。