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

我意识到参数化的SQL查询是构build包含用户input的查询时清理用户input的最佳方式,但是我不知道用户input和转义任何单引号以及用单引号包围整个string是什么问题。 代码如下:

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

用户input的任何单引号被replace为双单引号,这就消除了用户结束string的能力,所以他们可以input的任何内容,例如分号,百分号等都将是string的一部分,而不是实际上是作为命令的一部分执行的。 我们正在使用Microsoft SQL Server 2000,为此我认为单引号是唯一的string分隔符,也是唯一的方法来转义string分隔符,所以没有办法执行用户input的任何内容。

我看不出有什么办法对此发起SQL注入攻击,但是我意识到,如果这看起来像其他人可能已经想到的那样是无懈可击的,这将是常见的做法。 我的问题是:这个代码有什么问题? 有没有人知道通过这种消毒技术获得SQL注入攻击的方法? 使用这种技术的示例用户input将非常有帮助。

提前致谢。

更新:

感谢大家的回答; 我的研究中发现的所有信息几乎都出现在这个页面上,这表明那些忙于忙碌的日子里帮助我解决这个问题的人们的聪明才智和技巧。

我还没有接受任何答案的原因是,我仍然不知道有什么办法有效地发起这个代码的SQL注入攻击。 有些人build议反斜杠会转义一个单引号,而让另一个结束这个string,这样string的其余部分就会作为SQL命令的一部分被执行,而且我意识到这个方法可以将SQL注入一个mySQL数据库,但在MS SQL 2000中,唯一可以find的方法是转换单引号。 反斜杠不会这样做。 除非有办法停止单引号的转义,否则用户input的其余部分都不会被执行,因为它将被全部视为一个连续的string。

我明白,有更好的方法来消毒input,但我真的更感兴趣,了解为什么我上面提供的方法将无法正常工作。 如果有人知道任何具体的方式来对这个消毒方法进行SQL注入攻击,我很想看到它。

首先,这只是不好的做法。 inputvalidation总是必要的,但也总是如此。
更糟糕的是,黑名单validation总是有问题,明确和严格定义您接受的值/格式要好得多。 诚然,这并不总是可能的 – 但在一定程度上,它必须始终做到。
关于这个问题的一些研究论文:

要点是,你所做的任何黑名单(以及过于宽容的白名单)都可以被绕过。 我的论文的最后一个链接显示的情况下,即使引用转义可以绕过。

即使这些情况不适用于你,这仍然是一个坏主意。 而且,除非你的应用程序非常小,否则你将不得不面对维护,也许还需要一定的治理:你如何确保它在任何时间都能正确地执行?

正确的方法来做到这一点:

  • 白名单validation:types,长度,格式或可接受的值
  • 如果你想黑名单,就往前走。 引用转义是好的,但在其他缓解的背景下。
  • 使用Command和Parameter对象进行预处理和validation
  • 仅调用参数化查询。
  • 更好的是,专门使用存储过程。
  • 避免使用dynamicSQL,不要使用string连接来构build查询。
  • 如果使用SP,则还可以将数据库中的权限限制为仅执行所需的SP,而不是直接访问表。
  • 您也可以轻松validation整个代码库只通过SP访问数据库…

好的,这个回应将会涉及到这个问题的更新:

“如果有人知道对这个消毒方法进行SQL注入攻击的具体方法,我很乐意看到它。

现在,除了MySQL反斜杠转义之外 – 考虑到我们实际上正在讨论MSSQL,实际上还有3种可能的方式仍然是SQL注入代码

sSanitizedInput =“'”&Replace(sInput,“'”,“''”)&“'”

考虑到这些并不是所有的时候都是有效的,并且非常依赖于你周围的实际代码:

  1. 二阶SQL注入 – 如果基于从数据库中检索到的数据重buildSQL查询 ,数据会连续转义并可能间接SQL注入。 看到
  2. string截断 – (有点复杂) – 场景是你有两个字段,说一个用户名和密码,和SQL连接他们两个。 而且这两个领域(或者仅仅是第一个领域)在长度上都有很大的限制。 例如,用户名被限制为20个字符。 说你有这个代码:
 username = left(Replace(sInput, "'", "''"), 20) 

然后你得到的是用户名,转义,然后修剪为20个字符。 这里的问题 – 我会坚持我的报价在第20个字符(例如19 a之后),并且你的逃避报价将被削减(在第21个字符)。 然后是SQL

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

结合前面提到的格式不正确的用户名将导致密码已经在引号之外 ,并且直接包含有效载荷。
3. Unicode走私 – 在某些情况下,可能会传递一个高级的unicode字符, 看起来像一个引号,但不是 – 直到它到达数据库,突然间。 由于它不是一个报价,当你validation它,它会通过容易…看到我以前的反应更多的细节,并链接到原来的研究。

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

我意识到问题提出后很长一段时间,但..

对“引用参数”过程发起攻击的一种方法是使用string截断。 根据MSDN,在SQL Server 2000 SP4(和SQL Server 2005 SP1)中,太长的string将被安静地截断。

引用string时,string的大小会增加。 每个撇号都重复。 这可以用来将部分SQL部分推送到缓冲区之外。 所以你可以有效地修剪掉where子句的一部分。

这可能在“用户pipe理”页面情况下最为有用,在这种情况下,您可能会滥用“更新”语句而不执行所有应该执行的检查。

所以,如果你决定引用所有的参数,确保你知道string的大小,并看到它不会被截断。

我会build议去参数。 总是。 只是希望我可以在数据库中强制执行。 而作为一个副作用,你更有可能获得更好的caching命中,因为更多的语句看起来是相同的。 (在Oracle 8上这当然是真的)

input卫生不是你想要的一半。 使用你的整个屁股。 在文本字段上使用正则expression式。 TryCast你的数字到正确的数字types,并报告validation错误,如果它不工作。 searchinput中的攻击模式非常简单,比如“ – ”。 假设来自用户的所有input都是敌对的。

我在处理“高级search”function时使用了这种技术,从头开始构build查询是唯一可行的答案。 (例如:允许用户根据产品属性的无限制约束条件search产品,显示列及其允许值作为GUI控件,以减less用户的学习阈值。

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

这个障碍是,你必须确定哪些string包含用户input(总是潜在的恶意),哪些string是有效的SQL查询。 其中一个陷阱就是如果你使用数据库中的值 – 那些原来由用户提供的值? 如果是这样,他们也必须逃脱。 我的答案是在构buildSQL查询时尽可能晚地进行清理(但不要迟到)。

然而,在大多数情况下,参数绑定是要走的路 – 只是更简单。

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

怎么样像这样逃避string中的引号:\'

您的replace会导致:\''

如果反斜杠转义了第一个引号,那么第二个引号就结束了string。

简单的答案:它有时会工作,但不是所有的时间。 你想对你所做的一切使用白名单validation,但我意识到这并不总是可能的,所以你不得不用最好的黑名单去。 同样,你想在所有的事情中使用参数化的存储过程,但是再一次,这并不总是可能的,所以你不得不使用带参数的sp_execute。

围绕任何可用的黑名单(和一些白名单)也有办法。

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

如果你需要做这个快速解决scheme,让你有时间得到一个真正的地方,那就去做吧。 但是不要以为你是安全的。

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

如果您有可用的参数化查询,则应始终使用它们。 只需要一个查询就可以通过networking进行查询,而您的数据库处于危险之中。

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

编辑:这不是像不允许恶意用户closures带引号的标识符一样简单:

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

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

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

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

QUOTED_IDENTIFIER有许多方法可以closures,而不必知道它。 诚然,这不是你正在寻找的吸烟枪漏洞,但它是一个非常大的攻击面。 当然,如果你也逃脱了双引号 – 那么我们又回到了我们开始的地方。 ;)

如果您的防守失败

  • 该查询需要一个数字而不是一个string
  • 还有其他的方法可以代表一个单引号,包括:
    • 一个转义序列如\ 039
    • 一个unicode字符

(在后一种情况下,只有在您完成replace之后才能扩展)

帕特里克,你是在所有的input,甚至数字input周围加单引号? 如果你有数字input,但不是把单引号放在它周围,那么你有暴露。

什么丑陋的代码所有的用户input的sanitisation将是! 然后是SQL语句的笨重的StringBuilder。 准备好的语句方法会产生更清晰的代码,SQL注入的好处是一个很好的补充。

另外为什么重新发明轮子?

为什么不把单引号改成(看起来像)两个单引号,而不是把它改成一个撇号,一个引号,或者完全删除它?

无论哪种方式,这是一个kludge …特别是当你合法地拥有可能使用单引号的东西(如名称)…

注意:你的方法也假定每个在你的应用程序上工作的人总是记得在input到达数据库之前对input进行清理,这在大多数情况下可能是不现实的。

这可能有用,但是对我来说似乎有些狡猾。 我build议通过相反的正则expression式来validation每个string是否有效。

虽然你可能会发现一个适用于string的解决scheme,但是对于数字谓词,你还需要确保它们只传递数字(简单的检查是否可以被parsing为int / double / decimal?)。

这是很多额外的工作。

是的,如果…

在研究了这个话题之后,我认为按照你所build议的那样进行消毒是安全的,但是只有在这些规则下:

  1. 你永远不会允许来自用户的string值成为string文字以外的东西(即避免给出configuration选项:“在这里input额外的SQL列名称/expression式:”)。 除string(数字,date,…)以外的值types:将它们转换为其原生数据types,并为每种数据types的SQL文字提供一个例程。

    • SQL语句是有问题的validation
  2. 你要么使用nvarchar / nchar列(和前缀string文字与N )或进入varchar / char列的限制值只能为ASCII字符(例如创buildSQL语句时抛出exception)

    • 这样你将避免从CHAR(700)到CHAR(39)的自动撇号转换(也可能是其他类似的Unicode黑客入侵)
  3. 你总是validation值的长度,以适应实际的列长度(抛出exception,如果更长)

    • SQL Server中存在一个已知的缺陷,可以绕过截断时引发的SQL错误(导致无提示截断)
  4. 您确保SET QUOTED_IDENTIFIER始终为ON

    • 当心,它是在分析时间,即即使在不可访问的代码段生效

遵守这4点,你应该是安全的。 如果你违反了其中的任何一个,SQL注入的一个方法打开。