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

时间:2020-03-06 14:46:52  来源:igfitidea点击:

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

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

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

我没有发现对这种情况发起SQL注入攻击的任何方法,但是我意识到,如果这在我看来像防弹一样,那么其他人可能已经想到了,并且这将是普遍的做法。我的问题是:此代码有什么问题?有人知道通过这种清理技术来解决SQL注入攻击的方法吗?利用此技术的样本用户输入将非常有帮助。

更新:

感谢大家的回答;我在研究中遇到的所有信息几乎都显示在此页面的某个地方,这表明抽出忙碌时间来帮助我解决这个问题的人们的才智和技能。

我尚未接受任何答案的原因是,我仍然不知道有什么方法可以有效地对此代码发起SQL注入攻击。一些人建议反斜杠可以转义一个单引号,而让另一个反斜杠结束该字符串,以便该字符串的其余部分将作为SQL命令的一部分执行,并且我意识到该方法可以将SQL注入到一个mySQL数据库,但是在MS SQL 2000中,唯一可以逃脱单引号的方法是使用另一个单引号。反斜杠不会这样做。并且,除非有一种方法可以停止转义单引号,否则将不会执行其余的用户输入,因为所有输入都将被视为一个连续的字符串。

我知道有更好的方法可以清除输入,但是我真的更感兴趣于了解为什么我上面提供的方法行不通。如果有人知道针对此清理方法发起SQL注入攻击的任何特定方式,我很乐意看到它。

解决方案

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

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

这是很多额外的工作。

就像我们似乎知道的那样,这是一个坏主意。

像这样的字符串中的引号转义符呢?

替换将导致:''

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

清除用户输入的所有代码会是多么丑陋的代码!然后是笨拙的StringBuilder用于SQL语句。预准备的语句方法可以使代码更简洁,并且SQL Injection的好处是非常不错的。

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

与其将一个单引号更改为(看起来像)两个单引号,为什么不将其更改为单引号,引号或者将其完全删除呢?

无论哪种方式,这都有点麻烦……尤其是当我们合法拥有可以使用单引号的内容(例如名称)时……

注意:方法还假设,使用应用程序的每个人始终记得在输入数据库之前清除输入,这在大多数情况下可能不切实际。

如果我们有可用的参数化查询,则应始终使用它们。只需要一个查询就可以遍历整个网络,数据库就处于危险之中。

输入卫生不是我们想要的事。用你的整个屁股。在文本字段上使用正则表达式。尝试将数字转换为正确的数字类型,如果不起作用,则报告验证错误。在输入中搜索攻击模式非常容易,例如'-。假设用户的所有输入都是敌对的。

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

在处理"高级搜索"功能时,我已经使用了此技术,在该功能中,从头开始构建查询是唯一可行的答案。 (示例:允许用户基于对产品属性的无限限制集搜索产品,将列及其允许的值显示为GUI控件,以降低用户的学习阈值。)

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

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

但是,在大多数情况下,使用参数绑定是很简单的方法。

首先,这只是不好的做法。输入验证始终是必需的,但也总是很困难。
更糟糕的是,黑名单验证始终是有问题的,最好是明确定义并严格定义我们接受的值/格式。诚然,这并非总是可能的,但一定程度上必须始终做到这一点。
关于此主题的一些研究论文:

  • http://www.imperva.com/docs/WP_SQL_Injection_Protection_LK.pdf
  • http://www.it-docs.net/ddata/4954.pdf(披露,这最后一个是我的;))
  • https://www.owasp.org/images/d/d4/OWASP_IL_2007_SQL_Smuggling.pdf(基于先前的论文,该论文不再可用)

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

即使这些情况不适用于我们,这仍然不是一个好主意。而且,除非应用很小,否则我们将不得不进行维护,也许还要进行一定量的管理:如何确保始终无处不在正确地执行它?

正确的方法是:

  • 白名单验证:类型,长度,格式或者可接受的值
  • 如果我们想将其列入黑名单,请继续。引号转义是好的,但要在其他缓解措施的范围内。
  • 使用命令和参数对象进行准备和验证
  • 仅调用参数化查询。
  • 更好的是,仅使用存储过程。
  • 避免使用动态SQL,并且不要使用字符串连接来构建查询。
  • 如果使用SP,则还可以将数据库中的权限限制为仅执行所需的SP,而不能直接访问表。
  • 我们还可以轻松地验证整个代码库仅通过SP访问数据库...

简单的回答:有时会起作用,但并非总是如此。

我们想对所有操作都使用白名单验证,但是我知道这并不总是可能的,因此我们不得不选择最佳猜测黑名单。同样,我们想在所有内容中使用经过参数化的存储proc,但是再一次,这并非总是可行的,因此我们不得不将sp_execute与参数一起使用。

我们可以通过各种方法找到可用的黑名单(还有一些白名单)。

体面的文章在这里:http://www.owasp.org/index.php/Top_10_2007-A2

如果我们需要快速解决此问题,以便有时间让我们有一个真正的解决方案,请执行此操作。但是不要以为你很安全。

为了安全起见,有两种方法可以做到这一点,无一例外。准备好的语句或者实用的存储过程。

是的,直到有人运行SET QUOTED_IDENTIFIER OFF并对我们使用双引号之前,它应该可以正常工作。

The SQL Server Native Client ODBC driver and SQL Server Native Client OLE DB Provider for SQL Server automatically set QUOTED_IDENTIFIER to ON when connecting. This can be configured in ODBC data sources, in ODBC connection attributes, or OLE DB connection properties. The default for SET QUOTED_IDENTIFIER is OFF for connections from DB-Library applications.
  
  When a stored procedure is created, the SET QUOTED_IDENTIFIER and SET ANSI_NULLS settings are captured and used for subsequent invocations of that stored procedure.
  
  SET QUOTED_IDENTIFIER also corresponds to the QUOTED_IDENTIFER setting of ALTER DATABASE.
  
  SET QUOTED_IDENTIFIER is set at parse time. Setting at parse time means that if the SET statement is present in the batch or stored procedure, it takes effect, regardless of whether code execution actually reaches that point; and the SET statement takes effect before any statements are executed.

编辑:这不像不让恶意用户关闭引用的标识符那样简单:

有很多方法可以使QUOTED_IDENTIFIER处于关闭状态,而我们不必了解它。诚然,这不是我们要寻找的吸烟手段,但这是一个很大的攻击面。当然,如果我们还转义了双引号,那么我们又回到了开始的地方。 ;)

  • 查询需要数字而不是字符串
  • Unicode字符

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

(在后一种情况下,只有当我们完成替换后,它才必须进行扩展)

帕特里克(Patrick),我们是否要在所有输入(甚至是数字输入)周围添加单引号?如果我们有数字输入,但没有将单引号引起来,那么我们就有机会了。

"If anyone knows of any specific way to mount a SQL injection attack against this sanitization method I would love to see it."

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

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

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

  • 二阶SQL注入-如果根据转义后从数据库中检索到的数据重建SQL查询,则该数据将不经转义而级联,并且可以间接进行SQL注入。看
  • 字符串截断-(稍微复杂一点)-方案是我们有两个字段,例如用户名和密码,SQL将这两个字段串联在一起。并且这两个字段(或者仅第一个字段)都对长度有严格的限制。例如,用户名限制为20个字符。假设我们有以下代码:
username = left(Replace(sInput, "'", "''"), 20)

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

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

然后,我们得到的是用户名,先进行转义,然后再修剪为20个字符。这里的问题是我将引号括在第20个字符中(例如19个字符后),转义的引号将被删节(在第21个字符中)。然后是SQL

结合上述格式错误的用户名,将导致密码已经在引号之外,并且将直接包含有效负载。
3. Unicode走私在某些情况下,可以传递看起来像引号的高级unicode字符,但直到它到达数据库时才传递。由于验证时它不是报价,因此很容易...请参阅我以前的答复以获取更多详细信息,并链接到原始研究。

我知道在问了问题之后已经很长时间了,但是..

对" quote the arguments"过程发起攻击的一种方法是使用字符串截断。
根据MSDN,在SQL Server 2000 SP4(和SQL Server 2005 SP1)中,太长的字符串将被安静地截断。

当我们引用字符串时,字符串的大小会增加。每个撇号都重复。
然后,可以将其用于将SQL的某些部分推送到缓冲区之外。因此,我们可以有效地修剪where子句的各个部分。

这可能在"用户管理"页面方案中最有用,在这种情况下,我们可能会滥用" update"语句而不执行应做的所有检查。

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

段落数量不匹配