在PHP中全面阻止SQL注入式攻击


  一、 注入式 突击的类型

  可能存在许多不同类型的 突击动机,然而乍看上去, 仿佛存在更多的类型 。这是十分 实在的-假如 歹意消费者发现了一个 可以执行多个 查问的 步骤的话 。

  假如你的脚本正在执行一个SELECT指令,那么, 突击者 可以 胁迫显示一个表格中的每一行记录-通过把一个例如"1=1"这样的条件注入到WHERE子句中,如下所示(其中,注入 部分以粗体显示):

SELECT * FROM wines WHERE variety = 'lagrein' OR 1=1;'

  正如我们在前面所 探讨的,这 本身可能是很有用的信息,由于它揭示了该表格的一般 构造(这是一条一般的记录所不能实现的),以及潜在地显示包括 机密信息的记录 。

  一条更新指令潜在地 存在更直接的 挟制 。通过把其它属性放到SET子句中,一名 突击者 可以 批改目前被更新的记录中的任何字段,例如下面的例子(其中,注入 部分以粗体显示):

UPDATE wines SET type='red','vintage'='9999' WHERE variety = 'lagrein'

  通过把一个例如1=1这样的恒真条件增加到一条更新指令的WHERE子句中,这种 批改 规模 可以 扩大到每一条记录,例如下面的例子(其中,注入 部分以粗体显示):

UPDATE wines SET type='red','vintage'='9999 WHERE variety = 'lagrein' OR 1=1;'

  最惊险的指令可能是DELETE-这是不难想像的 。其注入技术与我们已经看到的 雷同-通过 批改WHERE子句来 扩大受影响的记录的 规模,例如下面的例子(其中,注入 部分以粗体显示):

DELETE FROM wines WHERE variety = 'lagrein' OR 1=1;'

  二、 多个 查问注入

  多个 查问注入将会加剧一个 突击者可能引起的潜在的 败坏-通过同意多条 毁坏性指令包括在一个 查问中 。在 使用MySQL数据库时, 突击者通过把一个 出人 意料之外的终止符插入到 查问中即可很方便实现这丝毫-此时一个注入的引号(单引号或双引号)标记 期冀变量的结尾; 而后 使用一个分号终止该指令 。现在,一个另外的 突击指令可能被增加到现在终止的原始指令的结尾 。最后的 毁坏性 查问可能看起来如下所示:

SELECT * FROM wines WHERE variety = 'lagrein';GRANT ALL ON *.* TO 'BadGuy@%' IDENTIFIED BY 'gotcha';'

  这个注入将 缔造一个新的消费者BadGuy并给予其网络特权(在全部的表格上 存在全部的特权);其中,还有一个"不祥"的口令被加入到这个 方便的 SELECT语句中 。假如你遵照我们在以往文章中的 提议-严格 制约该过程消费者的特权,那么,这应该 无奈工作,由于Web服务器守护程序不再 占有你撤回的 GRANT特权 。然而从 实际上讲,这样的一个 突击可能给予BadGuy 自由权力来实现他对你的数据库的任何操作 。

  至于这样的一个多 查问是不是会被MySQL服务器 解决, 论断并不唯一 。这其中的一些缘由可能是由于不同版本的MySQL所致,然而大多数状况却是由于多 查问存在的 模式所致 。 MySQL的 监督程序 彻底同意这样的一个 查问 。常用的MySQL GUI-phpMyAdmin,在最后 查问之前会复制出以往全部的内容,而且仅仅这样做 。

  然而,大多数的在一个注入上下文中的多 查问都是由PHP的mysql 扩大负责治理的 。幸好,默许状况下,它是不同意在一个 查问中执行多个指令的;试图执行两个指令(例如上面所示的注入)将会 方便地招致失败-不设置任何 舛误,而且没有生成任何输出信息 。在这种状况下, 只管PHP也只不过"规规矩矩"地实现其缺省行为,然而 确切 可以 掩护你免于大多数 方便的注入式 突击 。

  PHP5中的新的mysqli 扩大(参考http://php.net/mysqli),就象mysql一样,内在地也不 支撑多个 查问,不过却提供了一个mysqli_multi_query()函数以 支撑你实现多 查问-假如你 确切想这样做的话 。

  然而,关于SQLite-与PHP5绑定到一同的可嵌入的SQL数据库引擎(参考http://sqlite.org/和http: //php.net/sqlite)状况更为可怕,由于其易于 使用而吸引了大量消费者的关注 。在有些状况下,SQLite缺省地同意这样的多指令 查问,由于该数据库 可以优化批 查问,特殊是十分有效的批INSERT语句 解决 。然而,假如 查问的 后果为你的脚本所 使用的话(例如在 使用一个SELECT语句检索记录的状况下),sqlite_query()函数却不会同意执行多个 查问 。

  三、 INVISION Power BOARD SQL注入 懦弱性

  Invision Power Board是一个 驰名的论坛系统 。2005年五月6号,在登录代码中发现了一处SQL注入 懦弱性 。其发现者为GulfTech Security Research的James Bercegay 。

  这个登录 查问如下所示:

$DB->query("SELECT * FROM ibf_members WHERE id=$mid AND password='$pid'");

  其中,成员ID变量$mid和口令ID变量$pid被 使用下面两行代码从my_cookie()函数中检索出:

$mid = intval($std->my_getcookie('member_id'));$pid = $std->my_getcookie('pass_hash');

  在此,my_cookie()函数 使用下列语句从cookie中检索要求的变量:

return urldecode($_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

{

return urldecode($_COOKIE[$ibforums->vars['cookie_id'].$name]);

}

   通过这样的 修改之后,其中的 要害变量在"通过"全局clean_value()函数后被返回,而其它变量却未进行 审查 。

  现在,既然我们 大体了解了什么是SQL注入,它的注入原理以及这种注入的 懦弱程度,那么接下来,让我们探讨如何有效地预防它 。幸好,PHP为我们提供了 丰硕的资源, 因此我们有 充足的 信念预言,一个经 细心地彻底地 使用我们所推举的技术构建的 利用程序将会从你的脚本中 根本上 肃清任何可能性的SQL注入-通过在它可能造成任何 败坏之前"清理"你的消费者的数据来实现 。