在SQL Server中,如何通过SQL注入来避免单引号溢出的卫生问题?

为了解决这个问题,我很清楚参数化查询是最好的select,但是我在问什么使得我现在处于弱势的策略成为可能。 人们坚持下面的解决scheme不起作用,所以我正在寻找一个为什么它不会的例子。

如果在发送到SQL Server之前使用以下转义代码构builddynamicSQL,那么什么样的注入可以击败此?

string userInput= "N'" + userInput.Replace("'", "''") + "'" 

这里也回答了类似的问题,但我不相信任何答案都适用于此。

在SQL Server中不能使用“\”来转义单引号。

我相信SQL走私与Unicode( 这里概述)将被阻止的事实,即正在生产的string被标记为单引号之前的N的Unicode。 据我所知,没有其他字符集,SQL Server会自动转换为单引号。 没有一个没有转义的单引号,我不相信注射是可能的。

我不相信串截断也是一个可行的载体。 SQL Server肯定不会被截断,因为根据微软 , nvarchar的最大大小是2GB。 2 GB的string在大多数情况下是不可行的,在我的情况下是不可能的。

二次注射可能是可能的,但有可能的情况是:

  1. 所有进入数据库的数据都使用上述方法进行消毒
  2. 数据库中的值永远不会附加到dynamicSQL中(为什么要这样做呢?只要引用任何dynamicSQLstring的静态部分中的表值?)。

我并不是说这比使用参数化查询更好,或者是使用参数化查询的替代方法,但是我想知道我所概括的是如何易受攻击的。 有任何想法吗?

有一些情况下,这个逃生function将失败。 最明显的是没有使用单引号:

 string table= "\"" + table.Replace("'", "''") + "\"" string var= "`" + var.Replace("'", "''") + "`" string index= " " + index.Replace("'", "''") + " " string query = "select * from `"+table+"` where name=\""+var+"\" or id="+index 

在这种情况下,您可以使用双引号,“倒勾”来“爆发”。 在最后一种情况下,没有什么可以“突破”的,所以你可以只写1 union select password from users--或者攻击者希望的任何SQL有效载荷。

这个转义函数失败的下一个条件是如果在string转义之后得到一个子string( 是的,我已经发现了这样的漏洞):

 string userPassword= userPassword.Replace("'", "''") string userName= userInput.Replace("'", "''") userName = substr(userName,0,10) string query = "select * from users where name='"+userName+"' and password='"+userPassword+"'"; 

在这种情况下, abcdefgji'的用户abcdefgji'被转义函数转换为abcdefgji' ,然后通过子string转换回abcdefgji' 。 这可以通过将密码值设置为任何sql语句来利用,在这种情况下, or 1=1--将被解释为sql,并且用户abcdefgji'' and password=被解释为abcdefgji'' and password= 。 生成的查询如下所示:

 select * from users where name='abcdefgji'' and password=' or 1=1-- 

T-SQL和其他先进的SQL注入技术已经提到。 高级SQL注入在SQL Server应用程序中是一个很好的论文,如果你还没有阅读,你应该阅读它。

最后一个问题是unicode攻击。 这类漏洞是由于转义函数不知道多字节编码而造成的,攻击者可以利用这个漏洞“消耗”转义字符 。 对string添加“N”将不会起作用,因为这不会影响string中稍后的多字节字符的值。 但是,这种types的攻击是非常罕见的,因为数据库必须configuration为接受GBK unicodestring(我不确定MS-SQL是否可以这样做)。

二阶代码注入仍然是可能的,这种攻击模式是由信任攻击者控制的数据源创build的。 转义是用来表示控制字符作为他们的字符文字。 如果开发人员忘记了从select获得的值,然后在另一个查询中使用这个值,那么攻击者将会得到一个字符字面的单引号。

testing一切,不要信任。

有了一些额外的规定,上面的方法不容易被SQL注入。 攻击的主要载体是SQL走私。 当相似的unicode字符以意想不到的方式被翻译时(例如`改变为'),走私就会发生。 有几个地方的应用程序堆栈可能容易受到SQL走私。

  • 编程语言是否适当地处理unicodestring? 如果语言不是unicode意识到的,它可能会错误地将一个unicode字符中的一个字节识别为单引号并将其转义。

  • 客户端数据库库(例如ODBC等)是否适当地处理unicodestring? .Net框架中的System.Data.SqlClient呢,但从Windows 95时代的旧图书馆呢? 第三方ODBC库确实存在。 如果ODBC驱动程序不支持查询string中的unicode,会发生什么情况?

  • 数据库是否正确处理input? SQL的现代版本是免疫的,假设你使用N“,但是SQL 6.5呢? SQL 7.0? 我不知道有什么特别的漏洞,但是这对于二十世纪九十年代的开发者来说并不是什么大事。

  • 缓冲区溢出? 另一个问题是引用的string比原始string长。 在哪个版本的Sql Server中引入了2GB的input限制? 在此之前是什么限制? 在旧版本的SQL中,查询超出限制时发生了什么? 从networking库的angular度来看,查询长度是否有限制? 或者在编程语言中的string的长度?

  • 是否有任何语言设置影响Replace()函数中使用的比较? .Net总是对Replace()函数进行二进制比较。 情况总是如此吗? 如果.NET的未来版本支持在app.config级别覆盖该行为,会发生什么情况? 如果我们使用正则expression式而不是replace()来插入单引号? 计算机的区域设置是否会影响此比较? 如果行为发生了变化,它可能不会受到sql注入的影响,但是,它可能会在string到达​​数据库之前,通过将单引号的单引号字符更改为单引号来无意中编辑string。

因此,假设您在当前版本的.Net上使用C#中的System.String.Replace()函数,并将内置的SqlClient库与当前(2005-2012)版本的SQL Server相比较,那么您的方法是脆弱。 当你开始改变事情的时候,就不可能做出任何承诺。 参数化查询方法是效率,性能和(在某些情况下)安全性的正确方法。

警告以上的评论不是这种技术的认可。 还有其他几个非常好的原因,为什么这是生成SQL的错误方法。 但是,详细说明这个问题超出了这个问题的范围。

不要使用这种技术进行新的开发。

不要使用这种技术进行新的开发。

不要使用这种技术进行新的开发。

使用查询参数比转义引号更好,更容易和更快。


重申您的意见,我看到您承认参数化,但值得强调。 当你可以参数化时,你为什么要使用转义?

在高级SQL注入中在SQL Server应用程序中 ,search文本中的“replace”一词,并从这一点上读取一些示例,即开发人员在input用户input后无意中允许SQL注入攻击。


有一个边缘情况下用\转义引号会导致一个漏洞,因为\在一些字符集中变成有效多字节字符的一半。 但是这不适用于你的情况,因为\并不是逃避angular色。

正如其他人指出的那样,您可能还将dynamic内容添加到您的SQL中,而不是string文字或date文字。 表或列标识符由SQL中的"] Microsoft / Sybase中的[ ]分隔.SQL关键字当然没有任何分隔符。对于这些情况,我build议将值插入白名单

底线是逃避一种有效的防御,如果你能确保你一贯做到这一点。 这是一个风险:应用程序开发人员中的一个可能会省略一个步骤,并且不安全地进行string插入。

当然,其他方法(如参数化)也是如此。 他们只有一贯地执行才有效。 但是我发现使用参数更容易,而不是找出正确的转义types。 而开发人员更可能使用方便的方法,不会减慢速度。

如果用户提供的input被解释为命令,则会发生SQL注入。 这里的命令是指任何不被解释为可识别的数据types文字的东西。

现在,如果仅在数据文字中使用用户input,特别是仅在string文字中使用用户input,则只能将用户input解释为与string数据不同的内容,才能够离开string文字上下文。 对于string或Unicodestring文字,它是包含文字数据的单引号,而embedded的单引号需要用两个单引号表示。

所以要留下一个string文字上下文,需要提供一个单引号(sic),因为两个单引号被解释为string文字数据,而不是string文字结束分隔符。

因此,如果用户用两个单引号replace用户提供的数据中的任何单引号,则用户不可能离开string文字上下文。

SQL注入可以通过unicode进行。 如果networking应用有这样的url:

HTTP:// mywebapp /部件/代码= ABC

它生成类似select *的部件,其中Code ='ABC'

但是黑客进入这个:

http:// mywebapp / widgets /?代码= ABC%CA%BC;删除表部件 –

SQL将看起来像select*从小部件Code ='ABC';放置表部件 – '

并且SQL Server将运行两个SQL语句。 一个做select,一个做下降。 您的代码可能会将url编码的%CA%BC转换为Unicode字符U02BC,它是一个“Modifier letter撇号”。 .Net中的Replace函数不会将其视为单引号。 但是,Microsoft SQL Server将其视为单引号。 这是一个可能允许SQL注入的例子:

 string badValue = ((char)0x02BC).ToString(); badValue = badValue + ";delete from widgets--"; string sql = "SELECT * FROM WIDGETS WHERE ID=" + badValue.Replace("'","''"); TestTheSQL(sql); 

如果你正在做string连接,可能没有100%安全的方法。 你可以做的是尝试检查每个参数的数据types,如果所有的参数都通过了这种validation,那么继续执行。 例如,如果你的参数应该是inttypes,而你得到的东西不能被转换成int,那么就拒绝它。

如果您接受nvarchar参数,这不起作用。

正如其他人已经指出的。 最安全的方法是使用参数化查询。