来源:自学PHP网 时间:2015-04-17 10:15 作者: 阅读:次
[导读] 前言在我们的数据库应用里,为了避免SQL注入的攻击,应用通常需要对生成的SQL字串进行转义,也就是对组成字串的变量中的单引号进行转义,通常是写成双份,比如: select * from tabl...
前言在我们的数据库应用里,为了避免SQL注入的攻击,应用通常需要对生成的SQL字串进行转义,也就是对组成字串的变量中的单引号进行转义,通常是写成双份,比如: select * from table_name where name = $var; 假如 $var 是 laser's database 那么要将其转义成: laser''s database 这样最后的 SQL 字串是这样的: select * from table_name where name = 'laser''s database'; 这样,数据库通常要对整个输入串进行扫描,在输入串很长的时候,性能确实比较差,而且,更糟糕的是,在集群环境里,SQL串是通过 proxy 以字串的形式广播给所有节点进行查询的,那么这个时候的转义可能会变得很复杂:proxy需要一层转义,而还要兼顾各个节点自身是否需要转义的问题,尤其是在proxy和节点之间的协议是二进制和字符协议混合的时候,简直会让人抓破脑袋。 名词解释: SQL 注入(SQL inject),是通过某种手段可以让数据库执行意外的查询。比如,上面的查询输入一个: '';delete from table_name; 这样的字串,那么如果不对 $var 加以转义,前面的 SQL 会变成这样:select * from table_name where name ='';delete from table_name; 这个就等于删除了你的table_name表的所有内容!!! 分析问题
那么,这个问题除了扫描字串进行转义之外,就没有别的方法了么?答案当然是“有其他方法”! 对于 PostgreSQL 来说,我们可以利用 PostgreSQL 特有的 $ 引号表示法,接合好的散列算法,实现免转义的功能! 用一句话来描述的话,就是我们用SQL自身的散列值,做$引号的内容,以此来避免扫描和多层转义的问题。 解法
$(美元符引号 PostgreSQL 的$(美元符)引号表示法的是这样的: 所有在$和$之间的,以字母开头,包含字母数字的东西,都会被 PostgreSQL 看作是单引号的替代品,比如下面这些都是$(美元符)引号: $q$ $func$ $string$ $q0123456789ABCDEF$ 使用$(美元符)引号的时候,用其包围的字串内的单引号可以不用转义,比如我们经常这样写函数定义: CREATE OR REPLACE FUNCTION myQuote(integer) returns integer as $$ select id from table_name where name = $q$laser's function$q$ limit $1; $$ language sql; 注意里面字串的的 laser' function 中的单引号并没有使用 quote_literal() 函数进行转义,而是直接用 $q$ 做引号包围起来;这样我们的代码可以省略很多难懂的单引号。对比一下下面的不用$引号表示法的版本: CREATE OR REPLACE FUNCTION myQuote(integer) returns integer as ' select id from table_name where name = ''laser''''s function'' limit $1; ' language sql; 上面的单引号数目已经足以让人崩溃了,如果我们再想想通过另外一个字串传递进来。。。。 避免自身被命中
$引号乍一看只是为了减少单引号的使用量,但是其自身并不意味着就不用做任何转义,因为只要文本内容中出现了$引号,那么它自己也是需要转义的,比如下面这样的: select $$laser's\$\$$$; 是不能通过分析器的。这个时候我们必须选取一个在查询文本中不会出现的东西放在$引号中间,比如,上面那个例子可以这么写: select $q$laser's $$$q$; 现在问题来了,在用户可能输入任何东西的情况下,我们如何才能利用好这个特性呢? 进入脑海的真是散列算法,比如,md5,其实我们可以这样设计自己的$引号: $q||md5(raw sql string)||$ 也就是说,一个$(美元符),后面跟着一个字母q,后面跟着裸SQL串计算出来的md5值的文本形式(HEX转义),后面再跟着一个 $(美元符)组成我们的$引号。 实例
我们举一个例子,还是拿上面的 select * from table_name where name = $var; 来说,假如 $var 的数值是: laser's $quote 那么 md5(laser's $quote)=a4f09eb303320f5529b7a3d8d6869dc6 于是,我们的字串文本的$引号会是: $qa4f09eb303320f5529b7a3d8d6869dc6$ 然后,我们的裸SQL字串会是这个: select * from table_name where name = $qa4f09eb303320f5529b7a3d8d6869dc6$laser's $quote$qa4f09eb303320f5529b7a3d8d6869dc6$ 计算上面这个串的md5值,得到: 29750c173eee666ee4b9ca11f02c4dc5 于是整个SQL串的SQL文本表现形式为: $q29750c173eee666ee4b9ca11f02c4dc5$select * from table_name where name = $qa4f09eb303320f5529b7a3d8d6869dc6$laser's $quote$qa4f09eb303320f5529b7a3d8d6869dc6$$q29750c173eee666ee4b9ca11f02c4dc5$
laser=# select $q29750c173eee666ee4b9ca11f02c4dc5$select * from table_name where name = $qa4f09eb303320f5529b7a3d8d6869dc6$laser's $quote$qa4f09eb303320f5529b7a3d8d6869dc6$$q29750c173eee666ee4b9ca11f02c4dc5$; ?column? ------------------------------------------------------------------------------------------------------------ select * from table_name where name = $qa4f09eb303320f5529b7a3d8d6869dc6$laser's $quote$qa4f09eb303320f5529b7a3d8d6869dc6$ (1 笔资料列) 而且这样我们也可以直接避免转义了,很多场合下,我们可以不需要调用 quote 系列函数。 理论分析
为什么这样我们就可以避免转义呢?其实我们只是充分利用了散列算法的低碰撞概率的特性,众所周知,MD5 会生成128位的一个数值,理论上其碰撞概率应该是接近1/2^128,最近山东大学王小云教授证明这个碰撞概率比这个高多了,大概是在 1/2^96 左右,但是这个概率,对我们99.9999%的应用来说,依然是极低极低的;而且,我们未必一定要用MD5,也可以用更安全的 SHA 等算法,这样可以在$引号长度相同的情况下,获得超越 6σ的可靠性。 最后说两句性能,无论怎样,扫描还是要至少扫描一次的,但是长字串的扫描要远快于单字符的扫描,并且,MD5算法是块运算,一次读取16个字节,这方面,也会有一定的性能提升。 |
自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习
京ICP备14009008号-1@版权所有www.zixuephp.com
网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com