跳转到主要内容
zheojian 提交于 29 January 2013

原文链接:http://drupal.org/node/101495

输入,不管是来自访问者还是是服务端,都应该小心处理。

考虑下面从W. J. Gilmore's的 A Programmer's Introduction to PHP 4.0改写而来的例子。此例将用户输入的参数填入查询语句中。

<?php /** Example 1 - Insecure * SQL injection via $keyword */ $keyword = $_REQUEST['keyword']; $query = "SELECT cust_id, cust_name, cust_email FROM customers WHERE category = '$keyword'"; $result = db_query($query); ?>

Drupal哲学——在适当的时候转义或者过滤

在文本处理安全问题时,你会发现如下的专家意见:总是验证输入。但问题是,对于像Drupal这样的内容管理系统来说,输入是不明确的。有很多无谓的建议.比如,去掉分号,就像名称字段里面的('),来防止SQL注入。让我们祈祷这种系统里没有名为O'Reilly的用户。

另一个经常被吹捧的“解决方案”是过滤关键词。防止SQL注入的一种方法是在内容中禁止使用像SELECT或者DROP这样的SQL关键字。不幸的是,这样会妨碍用户使用'insert','select'和'update'等等在正常英语中并不少见的词汇。更糟的是,来自荷兰的用户再也不能提到licorice (也就是drop)这个词了。这显然算不上一个通用的(甚至是理智的)方法。

所以我们真正要做的是确保,无论任何数据,其内容不能被解释为SQL语句。为了做到这一点,我们使用数据库API提供的转义函数。我们在数据库层做转义,而不是直接在接收输入后做,因为它可能不是我们唯一需要转义(或过滤)的地方。(原文:We do this escaping in the database layer and not directly after receiving input as it may not be the only escaping (or filtering) we have to do.)

<?php /** Example 2 - Insecure * SQL injection via $keyword * XSS via $keyword and (possibly) $row->cust_name, $row->cust_email */ $keyword = $_REQUEST['keyword']; $query = "SELECT cust_id, cust_name, cust_email FROM customers WHERE category = '$keyword'"; $result = db_query($query); echo "

$keyword

": while ($row = db_fetch_object($result)) { echo "$row->cust_name, $row->cust_email "; } ?>

第二个例子,通过input filtering escaping来说明问题——虽然小但充斥着漏洞;$keyword必须经过两次不同目的的过滤或转义:防止SQL注入和跨站脚本(XSS)攻击。对输入的$keyword同时进行转义和过滤,会导致输出中出现多余的斜杠,或者$keyword里有引号的时候将不能成功搜素。(原文:Applying both filters directly to the input $keyword would result in extraneous slashes showing up in output, or failed search attempted with " in them.)

解决的办法是在需要时,使用适当的过滤器。例如,在发送纯文本到浏览器或混合纯文本与HTML之前,使用check_plain转义它。

<?php /** Example 2 - corrected */ $keyword = $_REQUEST['keyword']; $query = "SELECT cust_id, cust_name, cust_email FROM customers WHERE category = '%s'"; $result = db_query($query, $keyword); echo '

'. check_plain($keyword) .'

': while ($row = db_fetch_object($result)) { // Not very elegant, but making a point. echo check_plain($row->cust_name) .','. check_plain($row->cust_email) .''; } ?>