使用 filter_xss()
跨站脚本(XSS)是攻击网站的一种常用方式,攻击者可以向一个网页插入他/她自己的代码,然后使用这些代码进行各种破坏活动。
注意:XSS 攻击的例子,参看 http://ha.ckers.org/xss.html。
假定你允许用户向你的网站输入 HTML,期望他们这么输入
Hi! My name is Sally, and I…
但是他们输入了
哎哟!我们又学了一课:永远不要信任用户输入。下面是函数 filter_xss() 的签名:
filter_xss($string, $allowed_tags = array(’a’, ‘em’, ’strong’, ‘cite’, ‘code’, ‘ul’, ‘ol’, ‘li’, ‘dl’, ‘dt’, ‘dd’))
函数 filter_xss() 对传递给它的文本字符串进行以下操作:
1、它删掉奇怪的字符比如 NULL 和 Netscape4 JavaScript 脚本。
2、它确保 HTML 实体比如 & 形式正确。
3、它确保 HTML 标签和标签属性的形式正确。在本阶段,没有出现在允许列表中 — 也就是,filter_xss() 中的第2个参数 — 的标签,将被删除。属性 style 也被删除,这是由于通过覆盖 CSS 它能够影响页面的外观,或者通过将页面的背景颜色设为一个垃圾链接的颜色来隐藏内容。如果你熟悉正则表达式,并且能够记住 HTML 实体的字符编码的话,你可以使用一个调试器来一步一步的学习一下 filter_xss()(位于 modules/filter/filter.module)以及它的相关函数。
4、它确保所有的 HTML 标签都不包含未允许的协议。允许的协议有 http、https、ftp、news、nntp、telnet、mailto、irc、ssh、sftp 和 webcal。你可以通过设置 filter_allowed_protocols 变量来修改这一列表。通过将下面的代码添加到你的 settings.php 文件中(参看 settings.php 文件中的关于变量覆写的注释),可以讲允许的协议限制为 http 和 https:
$conf = array(
‘filter_allowed_protocols’ => array(’http’, ‘https’)
);
下面是来自于 aggregator.module 的使用 filter_xss() 的例子,该模块用来处理存在潜在安全隐患的 RSS 或者 Atom 种子。在这里模块准备展示一个节点:
function theme_aggregator_feed($feed) {
$output = ”;
$output .= theme(’feed_icon’, $feed->url) .”n”;
$output .= $feed->image;
$output .= ”.
aggregator_filter_xss($feed->description) .”n”;
$output .= ‘‘. t(’URL:’) .’ ‘ . l($feed->link, $feed->link, array(), NULL, NULL, TRUE) .”n”;
…
}
细心的读者会注意到在我们的例子 theme_aggregator_feed() 中的代码中我们调用了 l(),我们只向 l() 传递了一个 $feed->link 参数而没有做任何检查。这是因为 l() 函数为了方便在它内部调用 check_plain()。其它自动调用 check_plain() 的地方还有,当菜单钩子搜集菜单项的标题时,还有在 theme (’placeholder’) 中。除了这些情况,为了确保安全,你必须自己调用 check_plain()。
注意:我们调用了 aggregator_filter_xss(),它对 filter_xss() 进行了封装并提供了一组可接受的 HTML 标签。我们将这个函数稍微做了简化,如下所示:
/**
* Safely render HTML content, as allowed.
*/
function aggregator_filter_xss($value) {
$tags = variable_get(”aggregator_allowed_html_tags”, ‘
-
- ‘);
// Turn tag list into an array so we can pass it as a parameter.
$allowed_tags = preg_split(’/s+|<|>/’, $tags, -1, PREG_SPLIT_NO_EMPTY));
return filter_xss($value, $allowed_tags);
}
注意:作为安全性的一个练习,你可以拿出你自己的一些定制模块,来追踪进入系统了用户输入的存储,一定要保证在进行逻辑处理之前先对用户输入进行清理,以保证安全性。
使用filter_xss_admin()
有时你想让你的模块为后台管理页面生成 HTML。由于我们对后台管理页面进行了访问控制,所以我们可以假定这些可以访问后台管理页面的用户比普通用户更可信。你可以为后台管理页面建立一个特定 的过滤器并使用过滤器系统,但是这有点麻烦。因此,Drupal 提供了函数 filter_xss_admin()。它使用一组更加自由的允许的标签,简单的对 filter_xss() 做了封装,除了 和 标签以外,它包含了所有的其它标签。使用它的一个例子是在主题中展示站点的宗旨(mission):
if (drupal_is_front_page()) {
$mission = filter_xss_admin(theme_get_setting(’mission’));
}
只有在管理设置页面才可以设置站点的宗旨,而只有超级用户和具有“administer site configuration(管理站点配置)”权限的人才可以访问这一页面,所以在这里使用 filter_xss_admin() 就比较合适。
安全的处理URLs
模块常常处理用户提交的 URLs 并展示它们。我们需要一些机制来确保用户提供的值确实是一个合法的 URL。Drupal 提供了函数 check_url(),它实际上是仅仅对 filter_xss_bad_protocol() 做了封装。它通过检查来确保 URL 中的协议是该 Drupal 站点所允许的协议(参看“使用 filter_xss()”部分的第4点),并使用 check_plain() 来处理 URL。
如果你想决定一个 URL 是否合法,你可以使用 valid_url()。它将检查 http、https、和 ftp URL 的语法,并检查非法字符;如果 URL 通过了测试,那么它返回 TRUE。这是一个快速的方式用来确保提交的 URLs 不包含 javascript 协议。
警告:仅仅通过语法检查的 URL 并不一定安全!
如果你使用 URL — 例如,在一个查询字符串中 — 来传递一些信息的话,你可以使用 drupal_urlencode() 来传递转义了的字符。它是一个封装了 PHP 函数的例子:你可以直接调用 PHP 的 urlencode(),但这样你将失去由 Drupal 为你负责一个函数的好处。类似的字符串封装函数参看 unicode.inc;例如,使用 drupal_strlen() 来代替 PHP 函数 strlen()。