一、注入式攻击的类型 可能存在许多不同类型的攻击动机,但是乍看上去,似乎存在更多的类型。这是非常真实的-如果恶意用户发现了一个能够执行多个查询的办法的话。本文后面,我们会对此作详细讨论。 如果你的脚本正在执行一个SELECT指令,那么,攻击者可以强迫显示一个表格中的每一行记录-通过把一个例如"1=1"这样的条件注入到WHERE子句中,如下所示(其中,注入部分以粗体显示): SELECT*FROMwinesWHEREvariety='lagrein'OR1=1;'
正如我们在前面所讨论的,这本身可能是很有用的信息,因为它揭示了该表格的一般结构(这是一条普通的记录所不能实现的),以及潜在地显示包含机密信息的记录。
一条更新指令潜在地具有更直接的威胁。通过把其它属性放到SET子句中,一名攻击者可以修改当前被更新的记录中的任何字段,例如下面的例子(其中,注入部分以粗体显示): UPDATEwinesSETtype='red','vintage'='9999'WHEREvariety='lagrein'
通过把一个例如1=1这样的恒真条件添加到一条更新指令的WHERE子句中,这种修改范围可以扩展到每一条记录,例如下面的例子(其中,注入部分以粗体显示):
UPDATEwinesSETtype='red','vintage'='9999WHEREvariety='lagrein'OR1=1;'
最危险的指令可能是DELETE-这是不难想像的。其注入技术与我们已经看到的相同-通过修改WHERE子句来扩展受影响的记录的范围,例如下面的例子(其中,注入部分以粗体显示):
DELETEFROMwinesWHEREvariety='lagrein'OR1=1;'
二、多个查询注入 多个查询注入将会加剧一个攻击者可能引起的潜在的损坏-通过允许多条破坏性指令包括在一个查询中。在使用MySQL数据库时,攻击者通过把一个出乎意料之外的终止符插入到查询中即可很容易实现这一点-此时一个注入的引号(单引号或双引号)标记期望变量的结尾;然后使用一个分号终止该指令。现在,一个另外的攻击指令可能被添加到现在终止的原始指令的结尾。最终的破坏性查询可能看起来如下所示: SELECT*FROMwinesWHEREvariety='lagrein'; GRANTALLalign=centerbgColor=#e7e9e9border=1>
$DB->query("SELECT*FROMibf_membersWHEREid=$midANDpassword='$pid'"); 其中,成员ID变量$mid和口令ID变量$pid被使用下面两行代码从my_cookie()函数中检索出:
$mid=intval($std->my_getcookie('member_id')); $pid=$std->my_getcookie('pass_hash');
在此,my_cookie()函数使用下列语句从cookie中检索要求的变量:
returnurldecode($_COOKIE[$ibforums->vars['cookie_id'].$name]);
【注意】从该cookie返回的值根本没有被处理。尽管$mid在使用于查询之前被强制转换成一个整数,但是$pid却保持不变。因此,它很容易遭受我们前面所讨论的注入类型的攻击。
因此,通过以如下方式修改my_cookie()函数,这种脆弱性就会暴露出来: if(!in_array($name,array('topicsread','forum_read','collapseprefs'))) { return$this-> clean_value(urldecode($_COOKIE[$ibforums->vars['cookie_id'].$name])); } else { returnurldecode($_COOKIE[$ibforums->vars['cookie_id'].$name]); }
经过这样的改正之后,其中的关键变量在"通过"全局clean_value()函数后被返回,而其它变量却未进行检查。
现在,既然我们大致了解了什么是SQL注入,它的注入原理以及这种注入的脆弱程度,那么接下来,让我们探讨如何有效地预防它。幸好,PHP为我们提供了丰富的资源,因此我们有充分的信心预言,一个经仔细地彻底地使用我们所推荐的技术构建的应用程序将会从你的脚本中根本上消除任何可能性的SQL注入-通过在它可能造成任何损坏之前"清理"你的用户的数据来实现。 四、界定你的查询中的每一个值 我们推荐,你确保界定了你的查询中的每一个值。字符串值首当其冲,以及那些你通常期望应该使用"单"(而不是"双")引号的内容。一方面,如果你使用双引号来允许PHP在字符串内的变量替代,这样可以使得输入查询更为容易些;另一方面,这(无可否认,只是极少量地)也会减少以后PHP代码的分析工作。 下面,让我们使用我们一开始使用的那个非注入式查询来说明这个问题: SELECT*FROMwinesWHEREvariety='lagrein'
或以PHP语句表达为:
$query="SELECT*FROMwinesWHEREvariety='$variety'";
从技术上讲,引号对于数字值来说是不需要使用的。但是,如果你并不介意用引号把例如葡萄酒这样的一个域相应的一个值括起来并且如果你的用户把一个空值输入到你的表单中的话,那么,你会看到一个类似下面的查询:
SELECT*FROMwinesWHEREvintage=
当然,这个查询从语法上讲是无效的;但是,下面的语法却是有效的:
SELECT*FROMwinesWHEREvintage=''
第二个查询将(大概)不会返回任何果,但是至少它不会返回一个错误消息。 五、检查用户提交的值的类型
|