参数真的足以防止 Sql 注入吗?

Posted

技术标签:

【中文标题】参数真的足以防止 Sql 注入吗?【英文标题】:Are Parameters really enough to prevent Sql injections? 【发布时间】:2010-09-23 07:53:35 【问题描述】:

我一直在向我的同事和这里宣扬在 SQL 查询中使用参数的好处,尤其是在 .NET 应用程序中。我什至承诺他们会提供对 SQL 注入攻击的免疫力。

但我开始怀疑这是否真的是真的。是否有任何已知的 SQL 注入攻击可以成功针对参数化查询?例如,您可以发送一个导致服务器缓冲区溢出的字符串吗?

当然,要确保 Web 应用程序是安全的(例如清理用户输入和所有这些东西),还需要考虑其他因素,但现在我正在考虑 SQL 注入。我对针对 MsSQL 2005 和 2008 的攻击特别感兴趣,因为它们是我的主数据库,但所有数据库都很有趣。

编辑:澄清我所说的参数和参数化查询的意思。通过使用参数,我的意思是使用“变量”而不是在字符串中构建 sql 查询。 所以不要这样做:

SELECT * FROM Table WHERE Name = 'a name'

我们这样做:

SELECT * FROM Table WHERE Name = @Name

然后在查询/命令对象上设置@Name参数的值。

【问题讨论】:

我们应该澄清参数的含义(正如 Jonathan Leffler 指出的那样) - 我在想存储过程参数,但也有?参数和 0 参数... 说起来容易多了,我们不使用串联来构建查询。 由于标签是 asp.net,我假设您正在构建 Web 应用程序。在这种情况下,您还应该注意 XSS 攻击,也许还有其他攻击 【参考方案1】:

你可以运行动态sql作为例子

DECLARE @SQL NVARCHAR(4000);
DECLARE @ParameterDefinition NVARCHAR(4000);

SELECT  @ParameterDefinition = '@date varchar(10)'

SET @SQL='Select CAST(@date AS DATETIME) Date'

EXEC sp_executeSQL @SQL,@ParameterDefinition,@date='04/15/2011'

【讨论】:

【参考方案2】:

占位符足以防止注入。您可能仍然对缓冲区溢出持开放态度,但这是与 SQL 注入完全不同的攻击方式(攻击向量不是 SQL 语法,而是二进制)。由于传递的参数都将被正确转义,因此攻击者无法传递将被视为“实时”SQL 的数据。

您不能在占位符中使用函数,也不能将占位符用作列名或表名,因为它们被转义并作为字符串文字引用。

但是,如果您在动态查询中使用 parameters 作为 字符串连接 的一部分,您仍然容易受到注入,因为您的字符串不会被转义,但会字面意思。使用其他类型的参数(例如整数)是安全的。

也就是说,如果您使用使用输入来设置 security_level 之类的值,那么有人可以让自己成为您系统中的管理员,并且可以免费使用。但这只是基本的输入验证,与 SQL 注入无关。

【讨论】:

关键点是理解 Steve Lowe 的回答提出的问题,在@mikekidder 引用的文章中也指出——无论动态 SQL 在哪里,无论是在应用程序中还是在服务器。动态 SQL 很危险 - 但可以确保安全。 “攻击者无法传递将被视为“实时”SQL 的数据”。 - 这并不完全正确,请参见下面的示例。 以下所有示例都将“参数化查询”定义为 SQL 代码接受参数。正常定义是使用 DBMS 参数集合的查询。除非出现 DBMS 错误,否则后一种技术可以防止 SQL 注入。 我已经阅读了每一个链接。请引用任何指向针对 DBMS 参数集合的有效注入攻击的链接。实际上,您发布的链接专门将此方法称为击败 SQL 注入(请参阅“使用类型安全的 SQL 参数”部分)。 嗨!您能否提供指向 Oracle SQL 语法或类似内容的链接来证明该答案。我理解并完全同意你的看法,但如果能获得文档、语法等的官方链接,那就太好了 BestRegards,Raimbek【参考方案3】:

请记住,您可以使用参数轻松存储字符串,或者如果您没有任何策略,则说用户名,"); drop table users; --"

这本身不会造成任何伤害,但您最好知道该日期在您的应用程序中的进一步使用位置和方式(例如,存储在 cookie 中,稍后检索以执行其他操作。

【讨论】:

【参考方案4】:

这个帖子中关于“参数化查询”的定义似乎有些混乱。

SQL,例如接受参数的存储过程。 使用 DBMS 参数集合调用的 SQL。

鉴于前一个定义,许多链接显示有效的攻击。

但“正常”的定义是后一种。鉴于该定义,我不知道任何有效的 SQL 注入攻击。这并不意味着没有,但我还没有看到它。

从cmets,我表达的不够清楚,所以这里有一个例子,希望能更清楚:

这种方法对 SQL 注入开放

exec dbo.MyStoredProc 'DodgyText'

这种方法对 SQL 注入开放

using (SqlCommand cmd = new SqlCommand("dbo.MyStoredProc", testConnection))

    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter newParam = new SqlParameter(paramName, SqlDbType.Varchar);
    newParam.Value = "DodgyText";
    .....
    cmd.Parameters.Add(newParam);
    .....
    cmd.ExecuteNonQuery();

【讨论】:

您能澄清一下 DBMS 参数集合与接受参数的过程相比是什么意思吗? Rune,阅读此链接的“使用类型安全的 SQL 参数”部分:msdn.microsoft.com/en-us/library/ms161953.aspx 我对 Rune 的原始问题的回答是在更新之前对其进行了编辑。 我已经阅读并重读了有关 sql 注入的 msdn 文章,但我仍然看不出存储过程采用的参数与动态查询采用的参数之间有何区别。除了动态查询是动态的这一事实之外。还是得绑定参数吧? 它的区别在于绑定。如果直接调用带有参数的存储过程,则不会进行输入过滤。但是,如果您通过(例如)使用 .NET 中的 SqlCommand 参数集合进行绑定,则所有参数都将被过滤并视为纯文本。【参考方案5】:

不,当您将未经验证的数据插入 SQL 查询时,仍然存在 SQL 注入风险。

查询参数通过将文字值与 SQL 语法分开来帮助避免这种风险。

'SELECT * FROM mytable WHERE colname = ?'

这很好,但是将数据插入到不能使用查询参数的动态 SQL 查询中还有其他用途,因为它不是 SQL 值,而是表名、列名、表达式或其他一些语法。

'SELECT * FROM ' + @tablename + ' WHERE colname IN (' + @comma_list + ')'
' ORDER BY ' + @colname'

无论您是使用存储过程还是直接从应用程序代码执行动态 SQL 查询都没有关系。风险依然存在。

在这些情况下的补救措施是根据需要使用 FIEO

过滤输入:在插入数据之前验证数据看起来像合法的整数、表名、列名等。

转义输出:在这种情况下,“输出”是指将数据放入 SQL 查询中。我们使用函数来转换在 SQL 表达式中用作字符串文字的变量,以便对字符串中的引号和其他特殊字符进行转义。我们还应该使用函数来转换将用作表名、列名等的变量。至于其他语法,例如动态编写整个 SQL 表达式,这是一个更复杂的问题。

【讨论】:

【参考方案6】:

任何用于构造动态查询的字符串类型(varchar、nvarchar 等)的 sql 参数仍然存在漏洞

否则参数类型转换(例如到 int、decimal、date 等)应该消除任何通过参数注入 sql 的尝试

编辑:一个例子,其中参数@p1 是一个表名

create procedure dbo.uspBeAfraidBeVeryAfraid ( @p1 varchar(64) ) 
AS
    SET NOCOUNT ON
    declare @sql varchar(512)
    set @sql = 'select * from ' + @p1
    exec(@sql)
GO

如果从下拉列表中选择@p1,则它是潜在的sql注入攻击向量;

如果@p1 以编程方式制定而用户无法干预,则它不是潜在的 sql 注入攻击向量

【讨论】:

否;重点是传递给 DBMS 的字符串不是 SQL 语句的一部分。因此,字符串中的值对 SQL 的解释没有影响 - 只是对 SQL 引用的值。 这也是我看待参数的方式。他们应该防止这个问题。 Steven 是对的,例如,如果你将一个字符串传递给一个 sp,使用它来运行类似 sp_executeSql(sql server)的东西,那么你仍然有 sql 注入风险。 @Steven:这不是 SQL 的参数;您必须有一个占位符(问号)来代替字符串连接。并且 SQL 不允许您通过占位符指定表名。那是一个纯 SQL 注入漏洞 - 原始问题。 @Steven:也许“参数”这个词经常被重载一次。 :D【参考方案7】:

存储过程可能容易受到溢出/截断的特殊类型 SQL 注入的影响,请参阅:此处的数据截断启用注入:

http://msdn.microsoft.com/en-us/library/ms161953.aspx

【讨论】:

如果你仔细阅读这篇文章,你会发现使用 SQL Server 的参数集合可以防止这种攻击。这就是“参数化查询”的正常定义——它使用 DBMS 的参数集合。【参考方案8】:

如果您以任何形式或形式使用动态 sql,您的数据都是不安全的,因为权限必须在表级别。是的,您已经限制了来自该特定查询的注入攻击的类型和数量,但不限制用户在找到进入系统的方法时可以获得的访问权限,并且您完全可以访问内部用户访问他们不应该访问的内容以进行欺诈或窃取个人信息进行出售。任何类型的动态 SQL 都是危险的做法。如果您使用非动态存储过程,您可以在过程级别设置权限,除了过程定义的内容(当然系统管理员除外)之外,没有用户可以做任何事情。

【讨论】:

所以这里的教训是,如果你必须使用动态 sql,只能在存储过程中这样做。 +1 好建议! 否 -- 存储过程中的动态 SQL 仍然可以通过将未经验证的数据插入到动态查询中来引入 SQL 注入缺陷。 不,这里的教训是永远不要使用动态 SQl @HLGEM - 对了,汽车也涉及交通事故,所以我们千万不要使用汽车。 但是存储过程中的动态SQL(默认情况下)在调用者的许可下运行,不像静态SQL在存储过程所有者的许可下运行。这是一个重要的区别。【参考方案9】:

缓冲区溢出不是 SQL 注入。

参数化查询保证您可以安全地抵御 SQL 注入。他们不保证您的 SQL 服务器中不存在漏洞形式的漏洞,但没有任何保证。

【讨论】:

以上是关于参数真的足以防止 Sql 注入吗?的主要内容,如果未能解决你的问题,请参考以下文章

这个查询注入证明吗?

在codeigniter中逃避足够的防止sql注入的保护

带参数的存储过程如何防止 SQL 注入

使用prepared statement来防止SQL注入会影响性能吗?

这个参数化查询如何防止 SQL 注入?

MyBatis怎么防止SQL注入