drupal学习之-如何编写安全的代码

来源:互联网 发布:mac怎么输入英文标点 编辑:程序博客网 时间:2024/05/02 04:22

处理用户输入

当用户与Drupal交互时,一般都是通过一系列的表单比如节点提交表单、评论提交表单来完成的。用户也可能使用blogapi module来发布一个基于Drupal的日志。Drupal的用户输入方式可以总结为“存储原始的;过滤输出的”(store the original; filter on output)。数据库中总应该保存一份与用户输入完全一样的内容。当用户输入的内容准备用来生成web页面输出时,应该进行过滤。

当用户输入的内容在你的程序中被执行时,就可能引起安全漏洞。当你编写你的程序时,如果你没有全面的考虑各种情况的话,就会留下安全隐患。你可能期望用户输入标准的字符,而事实上他们可以输入非标准的字符串,比如控制字符。你可能看到其中包含字符%20的URL;例如,http://example.com/my%20document.html。这是一个为了与URL规范(参看http://www.w3.org/Addressing/URL/url-spec.html)兼容对空格字符编码后的结果。当有人保存了一个名为my document.html的文件并且可从web服务器上请求它,那么就会对空格编码。%意味着编码了的字符,而20则因为这它是ASCII的第20个字符。恶意用户可通过花招来使用编码字符给你的网站带来麻烦,你将会在本章的后面看到这一点。

 

考虑数据类型

当在一个像Drupal这样的系统中处理文本时,由于用户输入将会作为站点的一部分展示出来,所以将用户输入看做一个带有类型的变量能帮我们很好的理解这个系统。如果你使用过强类型语言比如JAVA变过程序,那么你将熟悉强类型变量。例如,在Java中一个整数就是一个整数。在PHP(弱类型语言)中,使用PHP的自动类型转换,根据上下文,你既可以把整数看做字符串也可以看做整数。但是优秀的PHP程序员都会仔细的考虑类型并恰到好处的利用自动类型转换。同样,尽管是通过用户输入得到的,节点提交表单中的“Body”(主体)字段,也可以作为一个文本进行处理,如果我们把它看做具有特定类型的文本,那么就会更好的理解的它的本质。用户输入的是纯文本么?用户输入的文本中是否带有HTML标签,如果带有的话是否将它们也一同显示出来?如果带有HTML标签的话,这些标签中是否允许带有恶意的标签,比如JavaScript,它可以将你的页面替换成一个手机铃声的广告?展示给用户的页面处在HTML格式中;用户输入是各种文本格式类型的变体,在展示它们以前必须安全的将其转化为HTML。如果我们使用这种方式来考虑用户输入的话,这能够帮助我们理解Drupal的文本转换功能的工作原理。文本输入的常见类型,还有将文本转化为另一种格式的函数,如表20-1所示。

 

表20-1 将一种文本类型安全的转化为另一种类型

Source Format     Target Format     Drupal Function   What It Does

Plain text         HTML               check_plain()           Encodes special characters

into HTML entities

HTML text         HTML               filter_xss()       Checks and cleans HTML

using a tag whitelist

Rich text          HTML               check_markup()     Runs text through filters

Plain text         URL                drupal_urlencode() Encodes special characters

into %0x

URL                HTML               check_url()        Strips out harmful protocols,

such as javascript:

Plain text         MIME               mime_header_encode() Encodes non-ASCII, UTF-8

encoded characters

 

 

Plain text 是仅仅包含纯文本的文本。例如,如果你让一个用户在一个表单中键入他/她喜欢的颜色,你期望用户输入“green”(绿色)或者 “purple”(紫色),而不包含任何标识字体。在另一个网页中包含这个输入框而不进行任何检查来确保它真的仅仅包含纯文本,那么就会留下安全漏洞。例如用户没有输入一个颜色,而输入了一下内容:

<img src="javascript:window.location ='<ahref="http://evil.example.com/133/index.php?s=11&">http://evil.example.com/133/index.php?s=11&</a>;ce_cid=38181161'">

因此,我们可以使用check_plain()来确保通过将HTML标签转义为HTML实体来消除潜在的危害。从check_plain()返回的文本不包含任何HTML标签,因为将它们转换为相应的实体了。

 

&lt;img src=&quot;javascript:window.location =&#039;&lt;a

href=&quot;http://evil.example.com/133/index.php?s=11&amp;&quot;&gt;http://evil.

example.com/133/index.php?s=11&amp;&lt;/a&gt;;ce_cid=38181161&#039;&quot;&gt;

HTML text 可以包含HTML标识字体。然而,你永远不要盲目的相信用户仅输入安全的HTML;一般情况下,你想将用户可用的标签限制在一个特定的有限集中。例如,<script>一般都会被你禁用,因为它允许用户在你的站点上运行他们选择的脚本。同样,你也不想让用户使用<form>标签在你的站点上建立表单。

Rich text是比纯文本包含更多信息的文本,它不一定必须是HTML。它可以包含wiki标识字体,或者论坛代码(BBCode),或者其它的标识语言。在展示以前,必须使用一个过滤器来将这些文本转化为HTML。

URL是由用户输入或者其它不可信的地方获取的URL。你可能希望用户输入http://example.com,但是用户却输入了javascript:runevilJS()。在将URL展现在HTML页面以前,你必须使用check_url()来检查它以确保它的格式良好并且不包含任何攻击。

 

注意 更过关于过滤器的信息,参看第11章。

 

使用check_plain()和t()

 

当你对你用到的文本不信任,并且你不想在文本中有任何markup(标识字体)时,使用check_plain()。

下面是使用用户输入的原始方式,假定用户刚刚在一个文本输入框中输入了一个喜欢的颜色。

 

下面的代码不安全:

drupal_set_message("Your favorite color is $color!"); // No input checking!

 

下面的代码安全,但不是最佳实践:

drupal_set_message('Your favorite color is ' . check_plain($color));

它是坏的代码,这是因为没有把这个文本字符串放到t()中,对于文本字符串总要调用该函数的。如果你像上面这样编写代码,那你就等着挨骂吧,翻译者将不能够翻译你的语句,因为它没有使用t()函数。

 

你不能将变量直接放到双引号中,并将它们传递给t()。下面的代码仍然不安全,因为没有使用占位符:

drupal_set_message(t("Your favorite color is $color!")); // No input checking!

 

t()函数提供了一种内置的方式用来确保你的字符串的安全性,使用一个带有单字符前缀的占位符,如下所示。

下面的代码很安全并且格式良好:

drupal_set_message(t('Your favorite color is @color', array('@color' => $color));

 

注意数组中的键(@color)与字符串中的占位符完全相同。

消息的结果如同下面的这样:

Your favorite color is brown.

 

前缀@告诉t()对替换占位符的值调用check_plain()。

注意 当运行一个Drupal 的t()时,将对占位符的值调用check_plain(),对于其它的字符串则不调用check_plain()。所以你需要信任你的翻译者。

 

在这里,我们可能想通过改变颜色值的样式来强调用户所选择的颜色。使用%前缀可以达成所愿,它意味着“对于该值执行theme('placeholder', $value)”。它间接的将值传递给了check_plain(),如图20-1所示。前缀%是最常用的前缀。

 

下面的代码是安全的并且格式良好:

drupal_set_message(t('Your favorite color is %color', array('%color' => $color));

 

一个消息产生的结果如下所示。使用theme_placeholder()对该值进行了主题化,它简单的使用了<em></em>标签对值进行了包装。

 

Your favorite color is brown.

 

如果你的文本在前面已被清理过了,你可以使用前缀!来禁用t()中的检查,然而如果你可以避免它的话,并不推荐这么做:

// l() function runs text through check_plain() and returns sanitized text

// so no need for us to do check_plain($link) or to have t() do it for us.

$link = l($user_supplied_text, $user_supplied_path);

drupal_set_message(t('Go to the website !website', array('!website' => $link));

 

在t()中的用于字符串替换的@, %, 和 !占位符的作用,如图20-1所示。在该图中我们是用了一个简单的例子进行说明,记住你可以在字符串中使用多个占位符并将它们添加到数组中。

原创粉丝点击