几乎每天我们都可以看到关于安全漏洞的头条消息,这种或者那种软件出现了漏洞。对于每个严谨的开发者来讲,将恶意用户拒之门外就是头等大事。
老葛的Drupal培训班 Think in Drupal
老葛的Drupal培训班 Think in Drupal
当在一个像Drupal这样的系统中处理文本时,由于用户输入将会作为站点的一部分展示出来,所以如果将用户输入看作一个带有类型的变量,那么就能帮我们很好的理解这个系统。如果你使用过强类型语言写过程序,比如JAVA,那么你将熟悉强类型变量。例如,在Java中,一个整数就是一个整数。在PHP(弱类型语言)中,使用PHP的自动类型转换,根据上下文,你既可以把整数看作字符串,也可以看作整数。但是优秀的PHP程序员都会仔细的考虑类型,并恰到好处的利用自动类型转换。同样,尽管是通过用户输入得到的,节点提交表单中的“Body”(主体)字段,也可以作为一个文本进行处理,如果我们把它看作具有特定类型的文本,那么就会更好的理解的它的本质。用户输入的是纯文本么?用户输入的文本中是否带有HTML标签,如果带有的话是否将它们也一同显示出来?如果带有HTML标签的话,这些标签中是否允许带有恶意的标签,比如JavaScript,它可以将你的页面替换成一个手机铃声的广告?在展示给用户的页面中,采用HTML格式;用户输入是各种文本格式类型的变体,在展示它们以前,必须安全的将其转化为HTML。如果我们使用这种方式来考虑用户输入的话,这能够帮助我们理解Drupal的文本转换功能的工作原理。文本输入的常见类型,还有将文本转化为另一种格式的函数,如表20-1所示。
老葛的Drupal培训班 Think in Drupal
Plain text(纯文本)
老葛的Drupal培训班 Think in Drupal
当你对你用到的文本不信任,并且你不想在文本中有任何markup(标识字体)时,那么就可以使用check_plain()。
跨站点脚本(XSS)是攻击网站的一种常用方式,攻击者可以向一个网页插入他/她自己的代码,然后使用这些代码进行各种破坏活动。
有时你想让你的模块为后台管理页面生成HTML。由于我们对后台管理页面进行了访问控制,所以我们可以假定这些可以访问后台管理页面的用户比普通用户更可信。你可以为后台管理页面建立一个特定的过滤器并使用过滤器系统,但是这有点麻烦。因此,Drupal提供了函数filter_xss_admin()。它使用一组更加自由的可用标签列表,简单的对filter_xss()做了封装,除了<script> 和 <style>标签以外,它包含了所有的其它标签。使用它的一个例子是在主题中展示站点的宗旨(mission):
模块常常处理用户提交的URLs并显示它们。我们需要一些机制来确保用户提供的值确实是一个合法的URL。Drupal提供了函数check_url(),它实际上是仅仅对filter_xss_bad_protocol()做了封装。它通过检查来确保URL中的协议是该Drupal站点所允许的协议(参看“使用filter_xss()阻止跨站点脚本攻击”部分的第5步),并使用check_plain()来处理URL。
攻击网站的一个常见方式称为SQL注入。让我们看一个没有考虑安全性的程序员编写的模块。他仅仅想用一种简单的方式,来列出特定类型节点的所有标题:
老葛的Drupal培训班 Think in Drupal
老葛的Drupal培训班 Think in Drupal
如果在你的SQL中,有多个只有在运行时才能确定的变量,这并不妨碍让你使用占位符。你将需要使用占位符比如'%s' 或者 %d,通过编程来创建你的SQL,接着你需要传递一组值来填充这些占位符。如果你自己直接调用db_escape_string(),那么你就做错了。下面的例子展示了占位符的使用,这里假定我们想取出,匹配特定节点类型的已发布节点的ID和标题:
当你编写自己的模块时,需要注意的另一个方面是“access arguments”键,你会在菜单钩子定义中的每个菜单项中用到它。在前面我们用来说明不安全代码的例子中,我们使用了下面的access参数:
老葛的Drupal培训班 Think in Drupal
在处理文件和文件路径时,Drupal面对的危险和其它web应用是一样的。
如果一个启用的模块允许文件上传,那么文件就应该放到一个特定的目录下,并通过代码来强制访问。
不要相信用户提供的文件名或者文件路径!当你编写一个模块时,你的代码期望能够收到somefile.txt,实际上它可能得到了其它文件,比如
老葛的Drupal培训班 Think in Drupal
并不是Drupal自带的所有文件,都是生产站点所必须的.例如,如果在一个生产站点上,有CHANGELOG.txt文件可用的话,那么互联网上的任何人都可以查看你的Drupal版本了(当然,一些高手黑客可用多种方式来探测你的网站是不是使用的Drupal;参看http://www.lullabot.com/articles/is-site-running-drupal)。表20-3列出了Drupal安装后,为了正常工作所需要的文件和目录;其它的都可以从生产站点上删除(不过要保留一份备份哦!)当然,你也可以采用另外一种方式,那就是限制对这些文件的读权限。
在Drupal中,有些周期性的调度任务是必须执行的,比如清理日志文件,更新统计等等。对于Unix系统,你可以使用cron任务,对于Windows,你可以使用任务调度器,来运行cron.php文件。可以通过命令行或者通过web服务器来运行该文件。在这个文件的执行中,它简单的做了一个完整的Drupal引导指令,并调用includes/common.inc中的drupal_cron_run()函数。这个函数使用信号量来阻止cron的重负运行(一个cron周期运行多次);尽管如此,特别小心的用户可能还想阻止任何用户访问http://example.com/cron.php。你可以通过在Drupal根目录下的.htaccess文件中添加以下代码,来实现这一点:
SSL支持
老葛的Drupal培训班 Think in Drupal
使用表单API的一个好处就是,它为你处理了许多安全性问题。例如,Drupal通过检查来保证,用户从下拉选择框中选择的值,确实是Drupal生成的选项中的一个。表单API使用了一系列的事件集,比如表单构建、验证、和执行。在验证阶段前面,你不能够使用用户输入,因为用户输入还没有被验证。例如,如果你使用的值来自$_POST,那么你就不能确保用户是否操作了该值。还有就是,使用#value元素在表单中传递信息,尽可能的使用它来代替隐藏域,因为恶意用户可以操作隐藏域,但是访问不了#value元素。
获取一个Drupal网站密码的最简单的方式是,打个电话给这个网站的秘书,这样说“你好,我是Joe。<一对客套话>。我是给你们的网站提供技术支持的,在技术支持中,我们遇到了问题。需要使用用户名和密码进行登录,你的用户名和密码是多少?”不幸的是,很多人会轻易的将这一私密信息告诉他人。尽管技术对此有所帮助,但是对于这种攻击,最好的办法还是对用户进行教育。
老葛的Drupal培训班 Think in Drupal
千万不要使用它。使用PHP函数eval(),你可能觉得它是进行元编程的极好的方式,或者想用它来减少多行代码,这个函数将一个字符串文本作为输入,并使用PHP解释器对其求值。这完全是一个错误。如果有任何方式允许一个用户使用eval()来操作输入的话,那么你就会将PHP解释器的威力暴露给用户。这距离泄露私密数据的时间也不会太长了,因为用户就可能使用这一方式来获取你的数据库中的用户名和密码。
读完本章后,你应该知道:
老葛的Drupal培训班 Think in Drupal