我意识到参数化SQL查询是构建包含用户输入的查询时消毒用户输入的最佳方式,但我想知道使用用户输入并转义任何单引号并用单引号包围整个字符串有什么问题.这是代码:
sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"
用户输入的任何单引号都被双单引号替换,这消除了用户结束字符串的能力,因此他们可能键入的任何其他内容(例如分号,百分号等)都将成为字符串的一部分而不是实际上作为命令的一部分执行.我们使用的是Microsoft SQL Server 2000,我相信单引号是唯一的字符串分隔符,也是逃避字符串分隔符的唯一方法,因此无法执行用户键入的任何内容.
我没有看到任何方法发起针对此的SQL注入攻击,但我意识到,如果这是像我看来一样的防弹,其他人会想到它,这将是常见的做法.我的问题是:这段代码有什么问题?有没有人知道通过这种清理技术获得SQL注入攻击的方法?利用此技术的示例用户输入将非常有用.
更新:
感谢大家的回答; 几乎我在研究中遇到的所有信息都出现在这个页面的某个地方,这表明那些在忙碌的日子里抽出时间帮助我解决这个问题的人的智慧和技巧.
我还没有接受任何答案的原因是我仍然不知道如何有效地针对此代码启动SQL注入攻击.有些人建议反斜杠会转义一个单引号而另一个引用字符串结束,以便字符串的其余部分作为SQL命令的一部分执行,我意识到这个方法可以将SQL注入一个mySQL数据库,但在MS SQL 2000中,唯一的方法(我能够找到)逃避单引号是另一个单一的qoute; 反斜杠不会这样做.除非有办法停止单引号的转义,否则其余的用户输入都不会被执行,因为它将全部作为一个连续的字符串.
我知道有更好的方法来消毒输入,但我真的更有兴趣了解为什么我上面提供的方法不起作用.如果有人知道对这种清理方法进行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访问数据库...
好的,这个回复将与问题的更新有关:
"如果有人知道对这种清理方法进行SQL注入攻击的任何特定方法,我很乐意看到它."
现在,除了MySQL反斜杠转义 - 并考虑到我们实际上是在谈论MSSQL,实际上还有3种方法可以让SQL注入你的代码
sSanitizedInput ="'"&Replace(sInput,"'","''")&"'"
考虑到这些并非一直都有效,并且非常依赖于您的实际代码:
二阶SQL注入 - 如果基于转义后从数据库中检索的数据重建SQL查询,则数据将被转义为未转义的,并且可以间接注入SQL.看到
字符串截断 - (有点复杂) - 场景是你有两个字段,比如用户名和密码,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字符,但不是 - 直到它到达数据库,突然之间.由于它在您验证时不是引用,它将通过简单...查看我之前的回复以获取更多详细信息,并链接到原始研究.
简而言之:永远不要查询逃避自己.你一定会犯错.相反,使用参数化查询,或者如果由于某种原因无法执行此操作,请使用为您执行此操作的现有库.没有理由自己做.
在问到这个问题之后我意识到这已经很久了,但是......
对'引用参数'过程发起攻击的一种方法是使用字符串截断.根据MSDN,在SQL Server 2000 SP4(和SQL Server 2005 SP1)中,一个太长的字符串将被悄然截断.
引用字符串时,字符串的大小会增加.每个撇号都会重复.然后,可以使用它将部分SQL推送到缓冲区之外.所以你可以有效地删除where子句的部分内容.
这可能在"用户管理"页面方案中非常有用,在这种情况下,您可以滥用"更新"语句而不执行它应该执行的所有检查.
因此,如果您决定引用所有参数,请确保您知道字符串大小发生了什么,并确保您不会遇到截断.
我建议使用参数.总是.希望我能在数据库中强制执行.作为副作用,您更有可能获得更好的缓存命中率,因为更多的语句看起来相同.(这在Oracle 8上确实如此)
我在处理"高级搜索"功能时使用了这种技术,从头开始构建查询是唯一可行的答案.(示例:允许用户根据对产品属性的无限制约束集搜索产品,将列及其允许值显示为GUI控件,以降低用户的学习阈值.)
它本身就是安全的AFAIK.然而,正如另一位回答者指出的那样,您可能还需要处理退格转义(尽管不是在使用ADO或ADO.NET将查询传递给SQL Server时,至少不能保证所有数据库或技术).
问题在于您必须确定哪些字符串包含用户输入(始终可能是恶意的),以及哪些字符串是有效的SQL查询.其中一个陷阱是如果您使用数据库中的值 - 这些值最初是由用户提供的吗?如果是这样,他们也必须逃脱.我的答案是在构造SQL查询时尽可能晚地尝试清理(但不能晚些!).
但是,在大多数情况下,参数绑定是可行的方法 - 它只是更简单.
输入卫生不是你想要的一半.用你的整个屁股.在文本字段上使用正则表达式.TryCast您的数字到正确的数字类型,并报告验证错误,如果它不起作用.在输入中搜索攻击模式非常容易,例如' - .假设来自用户的所有输入都是敌对的.
无论如何,你似乎知道这是一个坏主意.
如下所示:像字符串中的引号一样:\'
您的替换将导致:\''
如果反斜杠转义第一个引号,则第二个引号结束字符串.
简单的回答:它有时会起作用,但不是所有的时间.你想对你所做的一切使用白名单验证,但我意识到这并不总是可行的,所以你不得不使用最佳猜测黑名单.同样,您希望在所有内容中使用参数化存储过程,但再一次,这并不总是可行的,因此您不得不将sp_execute与参数一起使用.
你可以想出任何可用的黑名单(以及一些白名单).
这里有一篇不错的文章:http://www.owasp.org/index.php/Top_10_2007-A2
如果你需要做一个快速修复,让你有时间来实现一个真正的,做到这一点.但不要以为你是安全的.
有两种方法可以做到这一点,没有例外,可以安全地进行SQL注入; 准备好的语句或参数化的存储过程.