避免不带参数的SQL注入

Run*_*tad 108 c# sql-server asp.net sql-injection

我们正在讨论在我们的代码中使用参数化sql查询的另一个讨论.我们在讨论中有两个方面:我和其他一些人说我们应该总是使用参数来防止sql注入以及其他不认为有必要的人.相反,他们想要在所有字符串中用两个撇号替换单撇号以避免sql注入.我们的数据库都在运行Sql Server 2005或2008,我们的代码库运行在.NET framework 2.0上.

让我在C#中给你一个简单的例子:

我希望我们使用这个:

string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe
Run Code Online (Sandbox Code Playgroud)

而其他人想要这样做:

string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?
Run Code Online (Sandbox Code Playgroud)

SafeDBString函数定义如下:

string SafeDBString(string inputValue) 
{
    return "'" + inputValue.Replace("'", "''") + "'";
}
Run Code Online (Sandbox Code Playgroud)

现在,只要我们在查询中的所有字符串值上使用SafeDBString,我们就应该是安全的.对?

使用SafeDBString函数有两个原因.首先,它是自石头老化以来完成的方式,其次,由于您看到在数据库上运行的精确查询,因此更容易调试sql语句.

那么.我的问题是使用SafeDBString函数是否真的足以避免sql注入攻击.我一直试图找到破坏这种安全措施的代码示例,但我找不到任何示例.

那里有人可以打破这个吗?你会怎么做?

编辑: 总结到目前为止的答复:

  • 没有人找到办法在Sql Server 2005或2008上绕过SafeDBString.我觉得这很好吗?
  • 一些回复指出,使用参数化查询时可以获得性能提升.原因是查询计划可以重复使用.
  • 我们还同意使用参数化查询可以提供更易读的代码,更易于维护
  • 此外,总是使用参数比使用各种版本的SafeDBString,字符串到数字转换和字符串到日期转换更容易.
  • 使用参数可以获得自动类型转换,这在我们处理日期或十进制数时特别有用.
  • 最后:不要像JulianR写的那样尝试自己做安全.数据库供应商在安全性上花费了大量时间和金钱.我们没有办法做得更好,没有理由我们应该努力做好自己的工作.

因此,虽然没有人能够打破SafeDBString函数的简单安全性,但我得到了许多其他好的参数.谢谢!

Jul*_*anR 82

我认为正确的答案是:

不要试图自己做安全.使用任何可信赖的行业标准库,您可以尝试使用它,而不是自己尝试.无论您对安全做出什么假设,都可能是不正确的.尽管你自己的方法可能看起来很安全(而且它看起来最好看起来很不稳定),但是你有可能忽略某些东西并且你真的想在安全性方面抓住这个机会吗?

使用参数.


Mar*_*ell 72

然后有人去使用"而不是".参数是,IMO,唯一安全的方式.

它还避免了很多关于日期/数字的i18n问题; 什么日期是01/02/03?123,456多少钱?您的服务器(app-server和db-server)是否同意?

如果风险因素不能令他们信服,那么表现怎么样?如果使用参数,RDBMS可以重用查询计划,从而有助于提高性能.只有字符串才能做到这一点.

  • 实际上,无论您是否使用参数,sql server都可以重用查询计划.我同意其他参数,但在大多数情况下,参数化sql的性能参数不再存在. (5认同)
  • 如果查询文本对于先前的查询文本是IDENTICAL,则将重用查询计划.因此,如果您发送两次EXACT SAME查询,它将被重用.但是,如果您甚至只更改空格或逗号或其他内容,则必须确定新的查询计划. (4认同)

Mat*_*sen 27

这个论点是没有赢的.如果您确实设法找到漏洞,您的同事将只更改SafeDBString函数来解释它,然后要求您再次证明它是不安全的.

鉴于参数化查询是无可争议的编程最佳实践,证明的责任应该放在他们身上,以说明为什么他们没有使用既安全又更好的方法.

如果问题是重写所有遗留代码,那么容易妥协的是在所有新代码中使用参数化查询,并重构旧代码以在处理该代码时使用它们.

我的猜测是实际问题是骄傲和顽固,而你无能为力.


Joe*_*orn 19

首先,您的"替换"版本的样本是错误的.你需要在文本周围放置撇号:

string sql = "SELECT * FROM Users WHERE Name='" + SafeDBString(name) & "'";
SqlCommand getUser = new SqlCommand(sql, connection);
Run Code Online (Sandbox Code Playgroud)

这是参数为您做的另一件事:您不必担心值是否需要用引号括起来.当然,你可以在函数中构建它,但是你需要为函数添加很多复杂性:如何知道'NULL'和null之间的区别和'NULL'只是一个字符串,或者在数字和恰好包含大量数字的字符串.它只是bug的另一个来源.

另一件事是性能:参数化查询计划通常比连接计划更好地缓存,因此可能在运行查询时将服务器保存一步.

此外,逃避单引号还不够好. 许多数据库产品允许使用其他方法来转发攻击者可以利用的字符.例如,在MySQL中,您还可以使用反斜杠转义单引号.因此,以下"名称"值将仅使用该SafeDBString()函数炸毁MySQL ,因为当您将单引号加倍时,第一个仍然被反斜杠转义,使第二个"活动":

x \'或1 = 1; -


此外,JulianR提出了一个好点: 永远不要尝试自己做安全工作.它是那么容易得到的安全编程错误在这微妙的方式出现的工作,甚至进行全面测试.然后时间流逝,一年后你发现你的系统在六个月前被破解了,直到那时你才知道它.

始终尽可能地依赖为您的平台提供的安全库.它们将由以安全代码为生的人编写,比您可以管理的测试更好,并且如果发现漏洞,则由供应商提供服务.

  • replace函数添加了撇号 (5认同)
  • 然后它只是一个错误来源.它如何知道NULL作为空值和NULL作为文本字符串之间的区别?或者在数字输入和恰好包含数字的字符串之间? (5认同)

Jim*_*m T 10

所以我会说:

1)你为什么要重新实现内置的东西?它就在那里,随时可用,易于使用并已在全球范围内进行调试.如果在其中发现未来的错误,它们将被修复并且可以非常快速地提供给每个人,而无需您做任何事情.

2)有哪些流程可以保证永远不会错过对SafeDBString的调用?在一个地方错过它可能会打开一大堆问题.你会对这些事情有多大关注,并考虑当公认的正确答案如此容易达到时,这种努力会浪费多少.

3)你有多么确定你已经掩盖了微软(数据库的作者和访问库)在你的SafeDBString实现中所知道的每个攻击媒介......

4)读取sql的结构有多容易?该示例使用+连接,参数非常类似于string.Format,它更具可读性.

此外,还有两种方法可以解决实际运行的问题 - 滚动自己的LogCommand函数,一个没有安全问题的简单函数,甚至可以查看sql跟踪来计算出数据库认为真正发生的事情.

我们的LogCommand功能很简单:

    string LogCommand(SqlCommand cmd)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(cmd.CommandText);
        foreach (SqlParameter param in cmd.Parameters)
        {
            sb.Append(param.ToString());
            sb.Append(" = \"");
            sb.Append(param.Value.ToString());
            sb.AppendLine("\"");
        }
        return sb.ToString();
    }
Run Code Online (Sandbox Code Playgroud)

对或错,它为我们提供了我们需要的信息而没有安全问题.


Ste*_*ock 7

使用参数化查询,您可以获得的不仅仅是针对sql注入的保护.您还可以获得更好的执行计划缓存潜力.如果您使用sql server查询分析器,您仍然可以看到"在数据库上运行的确切sql",因此您在调试sql语句方面也没有真正丢失任何内容.


Red*_*ing 5

我已经使用这两种方法来避免SQL注入攻击,并且肯定更喜欢参数化查询.当我使用连接查询时,我使用了库函数来转义变量(比如mysql_real_escape_string),并且不相信我已经涵盖了专有实现中的所有内容(因为它似乎也是如此).

  • +1因为mysql_real_escape_string()转义了\ x00,\ x1a,\n\r'和".它还处理字符集问题.OP的同事天真的功能不会做任何事情! (2认同)