Drupal专业开发指南2022章.docx
- 文档编号:29177969
- 上传时间:2023-07-21
- 格式:DOCX
- 页数:81
- 大小:504.78KB
Drupal专业开发指南2022章.docx
《Drupal专业开发指南2022章.docx》由会员分享,可在线阅读,更多相关《Drupal专业开发指南2022章.docx(81页珍藏版)》请在冰豆网上搜索。
Drupal专业开发指南2022章
第20章编写安全的代码
(1)处理用户输入
编写安全的代码
几乎每天我们都可以看到关于这种或者那种软件具有安全漏洞的头条消息。
对于每个严禁的开发者来讲将恶意用户拒之门外是头等大事。
一个带有恶意的用户可以使用多种方式来攻击你的Drupal站点。
这些攻击方式中包括,向你的系统中注入代码并让它执行,操纵你数据库中的数据,查看用户无权访问的资料,通过你的Drupal站点发送垃圾邮件。
在本章,我们将学习如何编写安全的代码来阻止这些攻击。
幸运的是,Drupal提供了一些工具用来很容易的消除这些常见的安全漏洞。
处理用户输入
当用户与Drupal交互时,一般都是通过一系列的表单比如节点提交表单、评论提交表单来完成的。
用户也可能使用blogapimodule来发布一个基于Drupal的日志。
Drupal的用户输入方式可以总结为“存储原始的;过滤输出的”(storetheoriginal;filteronoutput)。
数据库中总应该保存一份与用户输入完全一样的内容。
当用户输入的内容准备用来生成web页面输出时,应该进行过滤。
当用户输入的内容在你的程序中被执行时,就可能引起安全漏洞。
当你编写你的程序时,如果你没有全面的考虑各种情况的话,就会留下安全隐患。
你可能期望用户输入标准的字符,而事实上他们可以输入非标准的字符串,比如控制字符。
你可能看到其中包含字符%20的URL;例如,URL规范(参看http:
//www.w3.org/Addressing/URL/url-spec.html)兼容对空格字符编码后的结果。
当有人保存了一个名为mydocument.html的文件并且可从web服务器上请求它,那么就会对空格编码。
%意味着编码了的字符,而20则因为这它是ASCII的第20个字符。
恶意用户可通过花招来使用编码字符给你的网站带来麻烦,你将会在本章的后面看到这一点。
考虑数据类型
当在一个像Drupal这样的系统中处理文本时,由于用户输入将会作为站点的一部分展示出来,所以将用户输入看做一个带有类型的变量能帮我们很好的理解这个系统。
如果你使用过强类型语言比如JAVA变过程序,那么你将熟悉强类型变量。
例如,在Java中一个整数就是一个整数。
在PHP(弱类型语言)中,使用PHP的自动类型转换,根据上下文,你既可以把整数看做字符串也可以看做整数。
但是优秀的PHP程序员都会仔细的考虑类型并恰到好处的利用自动类型转换。
同样,尽管是通过用户输入得到的,节点提交表单中的“Body(主体)”字段,也可以作为一个文本进行处理,如果我们把它看做具有特定类型的文本,那么就会更好的理解的它的本质。
用户输入的是纯文本么?
用户输入的文本中是否带有HTML标签,如果带有的话是否将它们也一同显示出来?
如果带有HTML标签的话,这些标签中是否允许带有恶意的标签,比如JavaScript,它可以将你的页面替换成一个手机铃声的广告?
展示给用户的页面处在HTML格式中;用户输入是各种文本格式类型的变体,在展示它们以前必须安全的将其转化为HTML。
如果我们使用这种方式来考虑用户输入的话,这能够帮助我们理解Drupal的文本转换功能的工作原理。
文本输入的常见类型,还有将文本转化为另一种格式的函数,如表20-1所示。
表20-1将一种文本类型安全的转化为另一种类型
SourceFormat-TargetFormat-DrupalFunction-WhatItDoes
Plaintext-HTML-check_plain()-EncodesspecialcharactersintoHTMLentities
HTMLtext-HTML-filter_xss()-ChecksandcleansHTMLusingatagwhitelist
Richtext-HTML-check_markup()-Runstextthroughfilters
Plaintext-URL-drupal_urlencode()-Encodesspecialcharactersinto%0x
URL-HTML-check_url()-Stripsoutharmfulprotocols,suchasjavascript:
Plaintext-MIME-mime_header_encode()-Encodesnon-ASCII,UTF-8encodedcharacters
Plaintext是仅仅包含纯文本的文本。
例如,如果你让一个用户在一个表单中键入他/她喜欢的颜色,你期望用户输入“green(绿色)”或者“purple(紫色)”,而不包含任何标识字体。
在另一个网页中包含这个输入框而不进行任何检查来确保它真的仅仅包含纯文本,那么就会留下安全漏洞。
例如用户没有输入一个颜色,而输入了一下内容:
因此,我们可以使用check_plain()来确保通过将HTML标签转义为HTML实体来消除潜在的危害。
从check_plain()返回的文本不包含任何HTML标签,因为将它们转换为相应的实体了。
window.location=' HTMLtext可以包含HTML标识字体。 然而,你永远不要盲目的相信用户仅输入安全的HTML;一般情况下,你想将用户可用的标签限制在一个特定的有限集中。 例如,一般都会被你禁用,因为它允许用户在你的站点上运行他们选择的脚本。 同样,你也不想让用户使用标签在你的站点上建立表单。 Richtext是比纯文本包含更多信息的文本,它不一定必须是HTML。 它可以包含wiki标识字体,或者论坛代码(BBCode),或者其它的标识语言。 在展示以前,必须使用一个过滤器来将这些文本转化为HTML。 URL是由用户输入或者其它不可信的地方获取的URL。 你可能希望用户输入,但是用户却输入了javascript: runevilJS()。 在将URL展现在HTML页面以前,你必须使用check_url()来检查它以确保它的格式良好并且不包含任何攻击。 注意: 更过关于过滤器的信息,参看第11章。 使用check_plain()和t() 当你对你用到的文本不信任,并且你不想在文本中有任何markup(标识字体)时,使用check_plain()。 下面是使用用户输入的原始方式,假定用户刚刚在一个文本输入框中输入了一个喜欢的颜色。 下面的代码不安全: drupal_set_message("Yourfavoritecoloris$color! ");//Noinputchecking! 下面的代码安全,但不是最佳实践: drupal_set_message('Yourfavoritecoloris'.check_plain($color)); 它是坏的代码,这是因为没有把这个文本字符串放到t()中,对于文本字符串总要调用该函数的。 如果你像上面这样编写代码,那你就等着挨骂吧,翻译者将不能够翻译你的语句,因为它没有使用t()函数。 你不能将变量直接放到双引号中,并将它们传递给t()。 下面的代码仍然不安全,因为没有使用占位符: drupal_set_message(t("Yourfavoritecoloris$color! "));//Noinputchecking! t()函数提供了一种内置的方式用来确保你的字符串的安全性,使用一个带有单字符前缀的占位符,如下所示。 下面的代码很安全并且格式良好: drupal_set_message(t('Yourfavoritecoloris@color',array('@color'=>$color)); 注意数组中的键(@color)与字符串中的占位符完全相同。 消息的结果如同下面的这样: Yourfavoritecolorisbrown. 前缀@告诉t()对替换占位符的值调用check_plain()。 注意: 当运行一个Drupal的t()时,将对占位符的值调用check_plain(),对于其它的字符串则不调用check_plain()。 所以你需要信任你的翻译者。 在这里,我们可能想通过改变颜色值的样式来强调用户所选择的颜色。 使用%前缀可以达成所愿,它意味着“对于该值执行theme('placeholder',$value)”。 它间接的将值传递给了check_plain(),如图20-1所示。 前缀%是最常用的前缀。 下面的代码是安全的并且格式良好: drupal_set_message(t('Yourfavoritecoloris%color',array('%color'=>$color)); 一个消息产生的结果如下所示。 使用theme_placeholder()对该值进行了主题化,它简单的使用了标签对值进行了包装。 Yourfavoritecolorisbrown. 如果你的文本在前面已被清理过了,你可以使用前缀! 来禁用t()中的检查,然而如果你可以避免它的话,并不推荐这么做: //l()functionrunstextthroughcheck_plain()andreturnssanitizedtext //sononeedforustodocheck_plain($link)ortohavet()doitforus. $link=l($user_supplied_text,$user_supplied_path); drupal_set_message(t('Gotothewebsite! website',array('! website'=>$link)); 在t()中的用于字符串替换的@、%和! 占位符的作用,如图20-1所示。 在该图中我们是用了一个简单的例子进行说明,记住你可以在字符串中使用多个占位符并将它们添加到数组中。 图20-1在字符串替换时,不同占位符前缀所起的作用 第20章编写安全的代码 (2)处理用户输入 使用filter_xss() 跨站脚本(XSS)是攻击网站的一种常用方式,攻击者可以向一个网页插入他/她自己的代码,然后使用这些代码进行各种破坏活动。 注意: XSS攻击的例子,参看http: //ha.ckers.org/xss.html。 假定你允许用户向你的网站输入HTML,期望他们这么输入 Hi! MynameisSally,andI... 但是他们输入了 哎哟! 我们又学了一课: 永远不要信任用户输入。 下面是函数filter_xss()的签名: filter_xss($string,$allowed_tags=array('a','em','strong','cite','code','ul','ol','li','dl','dt','dd')) 函数filter_xss()对传递给它的文本字符串进行以下操作: 1、它删掉奇怪的字符比如NULL和Netscape4JavaScript脚本。 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种子。 在这里模块准备展示一个节点: functiontheme_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标签。 我们将这个函数稍微做了简化,如下所示: /** *SafelyrenderHTMLcontent,asallowed. */ functionaggregator_filter_xss($value){ $tags=variable_get("aggregator_allowed_html_tags",' '); //Turntaglistintoanarraysowecanpassitasaparameter. $allowed_tags=preg_split('/\s+|<|>/',$tags,-1,PREG_SPLIT_NO_EMPTY)); returnfilter_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')); } 只有在管理设置页面才可以设置站点的宗旨,而只有超级用户和具有“administersiteconfiguration(管理站点配置)”权限的人才可以访问这一页面,所以在这里使用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、和ftpURL的语法,并检查非法字符;如果URL通过了测试,那么它返回TRUE。 这是一个快速的方式用来确保提交的URLs不包含javascript协议。 警告: 仅仅通过语法检查的URL并不一定安全! 如果你使用URL—例如,在一个查询字符串中—来传递一些信息的话,你可以使用drupal_urlencode()来传递转义了的字符。 它是一个封装了PHP函数的例子: 你可以直接调用PHP的urlencode(),但这样你将失去由Drupal为你负责一个函数的好处。 类似的字符串封装函数参看unicode.inc;例如,使用drupal_strlen()来代替PHP函数strlen()。 第20章编写安全的代码 (2)处理用户输入 使用filter_xss() 跨站脚本(XSS)是攻击网站的一种常用方式,攻击者可以向一个网页插入他/她自己的代码,然后使用这些代码进行各种破坏活动。 注意: XSS攻击的例子,参看http: //ha.ckers.org/xss.html。 假定你允许用户向你的网站输入HTML,期望他们这么输入 Hi! MynameisSally,andI... 但是他们输入了 哎哟! 我们又学了一课: 永远不要信任用户输入。 下面是函数filter_xss()的签名: filter_xss($string,$allowed_tags=array('a','em','strong','cite','code','ul','ol','li','dl','dt','dd')) 函数filter_xss()对传递给它的文本字符串进行以下操作: 1、它删掉奇怪的字符比如NULL和Netscape4JavaScript脚本。 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种子。 在这里模块准备展示一个节点: functiontheme_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标签。 我们将这个函数稍微做了简化,如下所示: /** *SafelyrenderHTMLcontent,asallowed. */ functionaggregator_filter_xss($value){ $tags=variable_get("aggregator_allowed_html_tags",' '); //Turntaglistintoanarraysowecanpassitasaparameter. $allowed_tags=preg_split('/\s+|<|>/',$tags,-1,PREG
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Drupal 专业 开发 指南 2022