我可以通过使用单引号转义单引号和周围用户输入来防止SQL注入吗?

时间:2008-09-26 12:41:26

标签: sql security sql-server-2000 sql-injection sanitization

我意识到参数化SQL查询是构建包含用户输入的查询时消毒用户输入的最佳方式,但我想知道使用用户输入并转义任何单引号并用单引号包围整个字符串有什么问题。这是代码:

sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"

用户输入的任何单引号都被双单引号替换,这消除了用户结束字符串的能力,因此他们可能键入的任何其他内容(例如分号,百分号等)都将成为字符串,而不是作为命令的一部分实际执行。我们使用的是Microsoft SQL Server 2000,我相信单引号是唯一的字符串分隔符,也是逃避字符串分隔符的唯一方法,因此无法执行用户键入的任何内容。

我没有看到任何方法针对此发起SQL注入攻击,但我意识到,如果这是像我看来一样的防弹,其他人会想到它,这将是常见的做法。我的问题是:这段代码有什么问题?有没有人知道通过这种清理技术获得SQL注入攻击的方法?利用这种技术的示例用户输入将非常有用。

更新:

感谢大家的回答;几乎我在研究中遇到的所有信息都出现在这个页面的某个地方,这表明那些在忙碌的日子里抽出时间帮助我解决这个问题的人的智慧和技巧。

我还没有接受任何答案的原因是我仍然不知道如何有效地针对此代码启动SQL注入攻击。有些人建议反斜杠会转义一个单引号而另一个引用字符串结束,以便字符串的其余部分作为SQL命令的一部分执行,我意识到这个方法可以将SQL注入一个mySQL数据库,但在MS SQL 2000中,唯一的方法(我能够找到)逃避单引号是另一个单一的qoute;反斜杠不会这样做。除非有办法停止单引号的转义,否则其余的用户输入都不会被执行,因为它将被视为一个连续的字符串。

我知道有更好的方法来消毒输入,但我真的更感兴趣的是为什么我上面提供的方法不起作用。如果有人知道对这种清理方法进行SQL注入攻击的任何特定方法,我很乐意看到它。

18 个答案:

答案 0 :(得分:82)

首先,这只是不好的做法。输入验证总是必要的,但也总是如此 更糟糕的是,黑名单验证总是有问题的,明确并严格定义您接受的值/格式要好得多。诚然,这并不总是可行的 - 但在某种程度上必须始终这样做 关于这一主题的一些研究论文:

重点是,您可以绕过任何黑名单(以及过于宽松的白名单)。我的论文的最后一个链接显示了甚至可以绕过报价转义的情况。

即使这些情况不适用于您,但这仍然是一个坏主意。此外,除非你的应用程序很小,否则你将不得不处理维护,也许需要处理一定数量的治理:你如何确保在任何地方都能正确完成?

正确的方法:

  • 白名单验证:类型,长度,格式或可接受的值
  • 如果你想黑名单,请继续。引用转义是好的,但在其他缓解措施的背景下。
  • 使用命令和参数对象,预填充和验证
  • 仅调用参数化查询。
  • 更好的是,仅使用存储过程。
  • 避免使用动态SQL,也不要使用字符串连接来构建查询。
  • 如果使用SP,您还可以将数据库中的权限限制为仅执行所需的SP,而不是直接访问表。
  • 您还可以轻松验证整个代码库仅通过SP访问数据库......

答案 1 :(得分:39)

好的,这个回复将与问题的更新有关:

  

“如果有人知道对这种清理方法进行SQL注入攻击的任何特定方法,我很乐意看到它。”

现在,除了MySQL反斜杠转义 - 并考虑到我们实际上是在谈论MSSQL,实际上还有3种方法可以让SQL注入你的代码

  

sSanitizedInput =“'”&替换(sInput,“'”,“''”)& “'”

考虑到这些并非一直都有效,并且非常依赖于您的实际代码:

  1. 二阶SQL注入 - 如果根据从数据库中转义后检索到的数据重建SQL查询,则数据将被转义为未转义的,并且可以间接进行SQL注入。见
  2. 字符串截断 - (有点复杂) - 场景是你有两个字段,比如用户名和密码,SQL连接它们。这两个领域(或者只是第一个领域)的长度都有一个硬性限制。例如,用户名限制为20个字符。说你有这个代码:
  3. username = left(Replace(sInput, "'", "''"), 20)
    

    然后你得到的是用户名,转义,然后修剪为20个字符。这里的问题 - 我会在第20个字符中引用我的引用(例如在19 a之后),并且你的转义引用将被修剪(在第21个字符中)。那么SQL

    sSQL = "select * from USERS where username = '" + username + "'  and password = '" + password + "'"
    

    结合上述格式错误的用户名将导致密码已经之外引号,并且只会直接包含有效负载。
     3. Unicode走私 - 在某些情况下,可以传递看起来的高级unicode字符,如引号,但不是 - 直到它到达数据库,突然。由于它在您验证时不是引用,它将通过简单...查看我之前的回复以获取更多详细信息,并链接到原始研究。

答案 2 :(得分:27)

简而言之:永远不要查询逃避自己。你一定会犯错。相反,使用参数化查询,或者如果由于某种原因无法执行此操作,请使用为您执行此操作的现有库。没有理由自己做。

答案 3 :(得分:17)

在问到这个问题后我意识到这已经很久了,但是......

对'引用参数'过程发起攻击的一种方法是使用字符串截断。 根据MSDN,在SQL Server 2000 SP4(和SQL Server 2005 SP1)中,一个太长的字符串将被悄然截断。

引用字符串时,字符串的大小会增加。每个撇号都会重复。 然后,可以使用它将部分SQL推送到缓冲区之外。所以你可以有效地删除where子句的部分内容。

这可能在“用户管理”页面方案中非常有用,在这种情况下,您可以滥用“更新”语句,而不执行它应该执行的所有检查。

因此,如果您决定引用所有参数,请确保您知道字符串大小发生了什么,并确保您不会遇到截断。

我建议使用参数。总是。希望我能在数据库中强制执行。作为副作用,您更有可能获得更好的缓存命中率,因为更多的语句看起来相同。 (这在Oracle 8上确实如此)

答案 4 :(得分:8)

输入卫生设施不是你想要的一半。用你的整个屁股。在文本字段上使用正则表达式。 TryCast您的数字到正确的数字类型,并报告验证错误,如果它不起作用。在输入中搜索攻击模式非常容易,例如' - 。假设用户的所有输入都是敌对的。

答案 5 :(得分:8)

我在处理'高级搜索'功能时使用了这种技术,从头开始构建查询是唯一可行的答案。 (例如:允许用户根据产品属性的无限制约束搜索产品,将列及其允许值显示为GUI控件,以降低用户的学习阈值。)

本身就是安全的AFAIK。然而,正如另一位回答者指出的那样,您可能还需要处理退格转义(尽管不是在使用ADO或ADO.NET将查询传递给SQL Server时,至少不能保证所有数据库或技术)。

问题在于您确实必须确定哪些字符串包含用户输入(始终可能是恶意的),以及哪些字符串是有效的SQL查询。其中一个陷阱是如果您使用数据库中的值 - 这些值最初是由用户提供的吗?如果是这样,他们也必须逃脱。我的答案是在构造SQL查询时尽可能晚地尝试清理(但不会更晚!)。

但是,在大多数情况下,参数绑定是可行的方法 - 它只是更简单。

答案 6 :(得分:6)

无论如何,你似乎知道这是一个坏主意。

像这样在字符串中转义引号之类的东西:\'

您的替换会导致:\''

如果反斜杠转义了第一个引号,则第二个引号结束了字符串。

答案 7 :(得分:5)

简单回答:它有时会起作用,但不是所有时间都起作用。 你想在你做的一切上使用白名单验证,但我意识到这并不总是可行的,所以你不得不使用最佳猜测黑名单。同样,您希望在所有内容中使用参数化存储过程,但是再一次,这并不总是可行,因此您不得不将sp_execute与参数一起使用。

您可以提出任何可用的黑名单(以及一些白名单)。

这是一个体面的写作:http://www.owasp.org/index.php/Top_10_2007-A2

如果你需要这样做作为一个快速修复,让你有时间来实现一个真正的,做到这一点。但不要以为你是安全的。

答案 8 :(得分:5)

有两种方法可以做到这一点,没有例外,可以安全地进行SQL注入;准备好的语句或参数化的存储过程。

答案 9 :(得分:4)

如果您有可用的参数化查询,则应始终使用它们。所需要的只是一个查询通过网络而你的数据库存在风险。

答案 10 :(得分:4)

是的,这应该可以正常运行,直到有人运行SET QUOTED_IDENTIFIER OFF并使用双引号。

编辑:这并不像不允许恶意用户关闭带引号的标识符那么简单:

  

SQL Server Native Client ODBC驱动程序和SQL Server的SQL Server Native Client OLE DB提供程序在连接时自动将QUOTED_IDENTIFIER设置为ON。这可以在ODBC数据源,ODBC连接属性或OLE DB连接属性中配置。 对于来自DB-Library应用程序的连接,SET QUOTED_IDENTIFIER的默认值为OFF。

     

创建存储过程时,将捕获 SET QUOTED_IDENTIFIER和SET ANSI_NULLS设置,并将其用于后续调用该存储过程

     

SET QUOTED_IDENTIFIER也对应于ALTER DATABASE的QUOTED_IDENTIFER设置。

     

SET QUOTED_IDENTIFIER 在分析时设置。在分析时设置意味着如果批处理或存储过程中存在SET语句,则无论代码执行是否实际到达该点,它都会生效;并且SET语句在任何语句执行之前生效。

有很多方法可以在没有你必须知道的情况下关闭QUOTED_IDENTIFIER。不可否认 - 这不是你正在寻找的吸烟枪,但它是一个非常大的攻击面。当然,如果你也逃过双引号 - 那么我们就回到了我们开始的地方。 ;)

答案 11 :(得分:4)

如果出现以下情况,您的辩护将失败:

  • 查询期待数字而不是字符串
  • 还有其他任何方式来表示单引号,包括:
    • 转义序列,例如\ 039
    • 一个unicode字符

(在后一种情况下,它必须是在你完成替换后才会扩展的东西)

答案 12 :(得分:4)

帕特里克,你在所有输入,甚至数字输入附近添加单引号?如果你有数字输入,但没有在它周围加上单引号,那么你有曝光。

答案 13 :(得分:1)

用户输入的所有卫生用品的丑陋代码是什么!然后是SQL语句的笨重的StringBuilder。准备好的语句方法可以产生更清晰的代码,SQL注入的好处是一个非常好的补充。

还有为什么重新发明轮子?

答案 14 :(得分:1)

不是将单引号更改为(看起来像什么)两个单引号,为什么不将其更改为撇号,引号或完全删除它?

无论哪种方式,它都有点像kludge ...特别是当你合法地拥有可能使用单引号的东西(如名字)时......

注意:您的方法还假设在您的应用程序上工作的每个人都会记住在输入到达数据库之前清理输入,这在大多数情况下可能是不现实的。

答案 15 :(得分:0)

虽然您可能会找到适用于字符串的解决方案,但对于数字谓词,您还需要确保它们只传递数字(简单检查是否可以将其解析为int / double / decimal?)。

这是一项额外的工作。

答案 16 :(得分:0)

它可能会奏效,但对我来说似乎有点儿了。我建议通过对正则表达式进行测试来验证每个字符串是否有效。

答案 17 :(得分:-1)

是的,你可以,如果......

在研究了这个主题之后,我认为你建议的输入消毒是安全的,但只有在这些规则下:

  1. 你永远不会允许来自用户的字符串值变成除字符串文字之外的其他内容(即避免给出配置选项:"在此输入其他SQL列名/表达式:")。字符串以外的值类型(数字,日期,...):将它们转换为本机数据类型,并为每种数据类型的SQL文字提供例程。

    • SQL语句验证有问题
  2. 您使用nvarchar / nchar列(并使用N前缀字符串文字)或限制进入varchar / char列的值仅限ASCII字符(例如,在创建SQL语句时抛出异常)

    • 这样你就可以避免从CHAR(700)到CHAR(39)的自动撇号转换(以及其他类似的Unicode黑客攻击)
  3. 总是验证值长度以适应实际列长度(如果更长则抛出异常)

    • SQL Server中存在一个已知缺陷,允许绕过截断时抛出的SQL错误(导致静默截断)
  4. 您确保SET QUOTED_IDENTIFIER始终为ON

    • 要注意,它在分析时生效,即使在无法访问的代码段中也是如此
  5. 遵守这4点,你应该是安全的。如果你违反其中任何一个,就会打开SQL注入的方法。