您的位置:首页 > 编程语言 > PHP开发

使用 PHP 构建的 Web 应用如何避免 XSS 攻击

2011-07-20 17:16 1011 查看
周婷(zhouting@cn.ibm.com),软件工程师,IBM中国软件开发技术实验室周婷,软件工程师,目前在IBM中国软件开发技术实验室从事刀片服务器管理固件的开发工作。您可以通过zhouting@cn.ibm.com和她联系。刘鑫(lxincdl@cn.ibm.com),软件工程师,IBM中国系统技术实验室刘鑫,目前在IBM中国系统技术实验室从事服务器管理固件的开发工作。刘坚(liujsh@cn.ibm.com),软件工程师,IBM中国系统技术实验室刘坚,软件工程师,Linux爱好者,目前在IBM中国软件开发技术实验室从事刀片服务器管理固件的开发工作。您可以通过liujsh@cn.ibm.com和他联系。简介:本文首先简单介绍开发测试人员如何对Web应用进行XSS漏洞测试,如何借助工具绕过客户端JavaScript校验输入恶意数据;然后针对使用PHP语言构建的Web站点,从在输出端对动态内容进行编码、以及在服务器端对输入进行检测两方面介绍如何避免恶意的XSS攻击。
本文的标签:php_(hypertext_preprocessor),安全
标记本文!发布日期:2009年3月19日
级别:初级
访问情况2573次浏览
建议:0(添加评论)使用PHP构建的Web应用如何避免XSS攻击
Web2.0的发展为网络用户的互动提供了更多机会。用户通过在论坛发表评论,或是在博客发表留言都可能有意或无意输入一些破坏性的内容,从而造成网页不能正常显示,影响其它用户的使用。XSS全称为CrossSiteScripting,因为CSS已经用作样式表的简称,故称为XSS。XSS是一种常见的网站攻击的方法。其原理是通过在网页的输入框输入一些恶意的内容,通常是JavaScript脚本片段,而这些恶意输入在提交之后并重新读回到客户端时,浏览器会解释执行这些恶意的脚本内容,从而影响网页的正常显示。
本文首先简单介绍开发测试人员如何对Web应用进行XSS漏洞测试,如何借助工具绕过客户端JavaScript校验输入恶意数据;然后针对使用PHP语言构建的Web站点,从在输出端对动态内容进行编码、以及在服务器端对输入进行检测两方面介绍如何避免恶意的XSS攻击。
回页首对Web应用进行XSS漏洞测试
测试路径
对WEB应用进行XSS漏洞测试,不能仅仅局限于在WEB页面输入XSS攻击字段,然后提交。绕过JavaScript的检测,输入XSS脚本,通常被测试人员忽略。下图为XSS恶意输入绕过JavaScript检测的攻击路径。

图1.XSS攻击测试路径–绕过JavaScript校验



常见的XSS输入
XSS输入通常包含JavaScript脚本,如弹出恶意警告框:
<script>alert("XSS");</script>

XSS输入也可能是HTML代码段,譬如:网页不停地刷新
<metahttp-equiv="refresh"content="0;">

嵌入其它网站的链接
<iframesrc=http://xxxxwidth=
25
0height=250></iframe>


XSS(CrossSiteScripting)CheatSheet维护了一份常见的XSS攻击脚本列表,可用来作为检测WEB应用是否存在XSS漏洞的测试用例输入。初次接触XSS攻击的开发人员可能会对列表提供的一些XSS输入不是很理解,本文第二部分将会针对不同代码上下文的XSS输入作进一步的解释。
测试工具
很多工具可以在浏览器发送Get/Post请求前将其截取,攻击者可以修改请求中的数据,从而绕过JavaScript的检验将恶意数据注入服务器。以下是一些常用的截取HTTP请求的工具列表。
Parosproxy(http://www.parosproxy.org)
Fiddler(http://www.fiddlertool.com/fiddler)
Burpproxy(http://www.portswigger.net/proxy/)
TamperIE(http://www.bayden.com/dl/TamperIESetup.exe)
笔者曾经使用TamperIE对WEB应用进行安全性测试。TamperIE小巧易用,能够截取IE浏览器发送的Get/Post请求,甚至能绕过SSL加密。不过TamperIE+IE7工作不稳定。IE7提供了对IPV6的支持,如果你并不计划测试你的Web应用对IPV6的支持,建议还是使用TamperIE+IE6的组合。
如图2所示:TamperIE绕过客户端浏览器JavaScript的校验,在POST请求提交时将其截取,用户可以任意修改表单输入项name和message的值,譬如将message的值修改为
"<script>alert(“XSShole!!”);</script>"
,然后点击”Sendaltereddata”按钮,将修改后的恶意数据发送给Web服务器。

图2.使用TamperIE截取Post请求



回页首在输出端对动态内容进行编码
对一个Web应用而言,其动态内容可能来源于用户输入、后台数据库、硬件状态改变或是网络信息等。动态内容特别是来自用户输入的动态内容很有可能包含恶意数据,从而影响网页的正常显示或是执行恶意脚本。将动态内容安全地显示在浏览器端与动态内容所处的上下文背景有关,譬如动态内容处在HTML正文、表单元素的属性、或是JavaScript代码段中。对于一个基于PHP语言的Web应用,当执行
"echo"
"print"
"printf"
"<?="
等语句时表示正在处理动态内容。本节将首先介绍PHP提供的库函数
htmlspecialchars()
的用法,此函数能将5个HTML特殊字符转化为可在网页显示的HTML实体编码;然后将介绍一些常见背景下的XSS攻击输入,以及如何在输出端对动态内容进行转义、编码从而避免XSS攻击。
使用PHP的htmlspecialchars()显示HTML特殊字符
从上文列举的XSS恶意输入可以看到,这些输入中包含了一些特殊的HTML字符如"<"、">"。当传送到客户端浏览器显示时,浏览器会解释执行这些HTML或JavaScript代码而不是直接显示这些字符串。<>&“等字符在HTML语言中有特殊含义,对于用户输入的特殊字符,如何直接显示在网页中而不是被浏览器当作特殊字符进行解析?
HTML字符实体由&符号、实体名字或者#加上实体编号、分号三部分组成。以下为HTML中一些特殊字符的编码。有的字符实体只有实体编号,没有对应的实体名字,譬如单引号。

表1.一些HTML特殊字符的实体编码
显示实体名字实体编号
<<<
>>>
&&&
""
N/A'
PHP提供了
htmlspecialchars()
函数可以将HTML特殊字符转化成在网页上显示的字符实体编码。这样即使用户输入了各种HTML标记,在读回到浏览器时,会直接显示这些HTML标记,而不是解释执行。
htmlspecialchars()
函数可以将以下五种HTML特殊字符转成字符实体编码:
&转成&
“转成"
<转成<
>转成>
‘转成'
当直接调用
htmlspecialchars($str)
时,&"<>被转义。
当设置ENT_QUOTES标记时,即调用
htmlspecialchars($str,ENT_QUOTES)
时,单引号也被转义。
当设置ENT_NOQUOTES标记时,单引号和双引号都不会被转义。即调用
htmlspecialchars($str,ENT_NOQUOTES)
时,只有&<>被转义。
不同背景下的动态内容的XSS攻击及解决方案
XSS攻击输入与动态内容所处的代码背景相关,譬如动态内容为表单元素属性的值、位于HTML正文、或是Javascript代码段中等等。
HTML标记的属性为动态内容
Web应用中,"input"、"style"、"color"等HTML标记的属性都可能为动态内容,其中"input"标记的"value"属性通常为动态内容。
例子1
<form…><INPUTtype=textname="msg"id="msg"size=10maxlength=8
value="<?=$msg?>"></form>
攻击XSS输入
Hello"><script>evil_script()</script>
将动态内容替换
$msg
替换为恶意XSS输入:
<form…><INPUTtype=textname="msg"id="msg"size=10maxlength=8
value="Hello"><script>evil_script()</script>"></form>
例子2
<form…><INPUTtype=textname="msg"id="msg"size=10
maxlength=8value=<?=$msg?>></form>
攻击XSS输入
Helloonmouseover=evil_script()
将动态内容替换
$msg
替换为恶意XSS输入:
<form…><INPUTtype=textname="msg"id="msg"size=10
maxlength=8value=Helloonmouseover=evil_script()></form>
分析
从例子1可以看到其XSS攻击输入中包含了HTML特殊字符<>"
从例子2可以看到其XSS攻击输入中没有包含上节中提到的五种HTML字符,但是"value"属性值没有使用双引号包围。
解决方案
调用
htmlspecialchars($str,ENT_QUOTES)
将以下5种HTML特殊字符<>&‘“转义;同时使属性值被双引号包围。譬如:
<form…><INPUTtype=textname="msg"id="msg"size=10
maxlength=8value="<?=htmlspecialchars($msg,ENT_QUOTES))?>"></form>
注意事项
将input的value进行转义,必须考虑显示和存储数据的一致性问题,即显示在浏览器端和存储在服务器端后台的数据可能因为转义而变得不一致。譬如存储在服务器端的后台原始数据包含了以上5种特殊字符,但是没有转义,为了防止XSS攻击,在浏览器端输出时对HTML特殊字符进行了转义:
1.当再度将表单提交时,存储的内容将会变成转义后的值。
2.当使用JavaScript操作表单元素,需要使用到表单元素的值时,必须考虑到值可能已经被转义。
HTML文本为动态内容
例子
<b>欢迎:<?=$welcome_msg?></b>
攻击XSS输入
<script>evil_script()</script>
将动态内容替换
$welcome_msg
替换为恶意XSS输入:
<b>欢迎:<script>evil_script()</script></b>
分析
在HTML正文背景下,<>字符会引入HTML标记,&可能会认为字符实体编码的开始,所以需要将<>&转义
解决方案
为简洁起见,直接使用
htmlspecialchars()
将5种HTML特殊字符转义,如:
<b>欢迎:<?=htmlspecialchars($welcome_msg,,ENT_NOQUOTES)?></b>
URL的值为动态内容
Script/Style/Img/ActiveX/Applet/Frameset…等标记的src或href属性如果为动态内容,必须确保这些URL没有指向恶意链接。
例子1
<scriptsrc=<?="$script_url>">
攻击XSS输入
http://evil.org/evil.js
将动态内容替换
$script_url
替换为恶意XSS输入:
<scriptsrc="http://evil.org/evil.js">
例子2
<imgsrc=”<?=$img_url>”>
攻击XSS输入
javascript:evil_script()
将动态内容替换
$img_url
替换为恶意XSS输入:
<imgsrc=”javascript:evil_script()”>
分析
一般情况下尽量不要让URL的值被用户控制。如果用户需要自己定义自己的风格及显示效果,也不能让用户直接控制整个URL的内容,而是提供预定义好的风格供用户设置、装配,然后由后台程序根据用户的选择组合成安全的URL输出。
字符集编码
浏览器需要知道字符集编码才能正确地显示网页。如果字符集编码没有显式在content-type或meta中定义,浏览器会有算法猜测网页的字符集编码。譬如
<script>alert(document.cookie)</script>
的UTF-7编码为:
+ADw-script+AD4-alert(document.cookie)+ADw-/script+AD4-
如果
+ADw-script+AD4-alert(document.cookie)+ADw-/script+AD4-
作为动态内容位于网页的顶端并传送到浏览器端,IE会认为此网页是UTF-7编码,从而使网页不能正常显示。
解决方案
显式定义网页的字符集编码,譬如
<metahttp-equiv=content-typecontent="text/html;charset=UTF-8">
动态内容为JavaScript事件处理函数的参数
JavaScript事件处理函数如onClick/onLoad/onError/onMouseOver/的参数可能包含动态内容。
例子
<inputtype="button"value="goto"onClick='goto_url("<?=$target_url>");'>
攻击XSS输入
foo");evil_script("
将动态内容替换
HTML解析器会先于JavaScript解析器解析网页,将
$target_url
替换为恶意XSS输入:
<inputtype="button"value="goto"onClick='goto_url("foo");evil_script("");'>
动态内容位于JavaScript代码段中
例子
<SCRIPTlanguage="javascript1.2">
varmsg='<?=$welcome_msg?>';
//…
</SCRIPT>
攻击XSS输入1
Hello';evil_script();//
将动态内容替换
$welcome_msg
替换为恶意XSS输入:
<SCRIPTlanguage="javascript1.2">
varmsg='Hello';evil_script();//';
//…
</SCRIPT>
攻击XSS输入2
Hello</script><script>evil_script();</script><script>
将动态内容替换
$welcome_msg
替换为恶意XSS输入:
<script>varmsg='Hello</script>
<script>evil_script();</script>
<script>'//...//dosomethingwithmsg_text</script>
分析
如上文所示,在JavaScript背景中使用动态内容需要非常谨慎。一般情况下,尽量避免或减少在Javascript的背景下使用动态内容,如果必须使用动态内容,在开发或代码审计时必须考虑这些动态内容可能的取值,是否会导致XSS攻击。
回页首建立PHP库函数校验输入
Web开发人员必须了解,仅仅在客户端使用JavaScript函数对非法输入进行检测过滤对于构建安全的WEB应用是不够的。如上文所述,攻击者可以轻易地借助工具绕过JavaScript校验甚至SSL加密输入恶意数据。在输出端对动态内容进行编码也只能起到一种双重保护的作用,更重要的应该在服务器端对输入进行校验。PHP提供了
strpos()
strstr()
preg_match()
等函数可用于检测非法字符和字符串;
preg_replace()
函数可用于替换非法字符串。OWASPPHPFilters开源项目提供了一些PHP库函数用于过滤非法输入可作为参考。一些常见的检测和过滤包括:
输入是否仅仅包含合法的字符;
输入如果为数字,数字是否在指定的范围;
输入字符串是否超过最大长度限制;
输入是否符合特殊的格式要求,譬如email地址、IP地址;
不同的输入框在逻辑上存在的耦合和限制的关系;
除去输入首尾的空格;
回页首总结
Web应用的安全性是一个很重要、覆盖范围很广泛的主题。为了防止常见的XSS的攻击,Web开发人员必须明白不能仅仅只在客户端使用JavaScript对输入进行检测、过滤;同时还应建立服务器端的输入校验、输出编码库函数;在服务器端检测、过滤输入;根据动态内容所处的背景将特殊字符进行编码后再传送给浏览器端显示。

参考资料
参考“XSSCheatSheet”,列举了一些常见的XSS攻击脚本,可用作XSS漏洞测试用例输入

查看“PHP函数手册”,了解htmlspecialchars()API。

参考“HTML字符实体编码”,了解HTML字符实体编码。

参考“开放式Web应用程序安全项目”社区,了解更多Web安全相关内容。

访问“TamperIE“,获得TamperIE,可用作XSS漏洞测试工具。

参考“XSSPreventionCheatSheet“,了解防止XSS攻击的一些规则。

访问“OWASPPHPFilters“,可获得开源的用于过滤非法输入的PHP库函数以供参考

查看“推荐PHP读物列表”。

浏览developerWorks上的所有PHP内容。

通过查阅IBMdeveloperWorks的PHP项目资源提高您的PHP技能。

访问developerWorks开放源码专区,获得丰富的how-to信息、工具和项目更新,帮助您用开放源码技术进行开发,并与IBM产品结合使用。

作者简介
周婷,软件工程师,目前在IBM中国软件开发技术实验室从事刀片服务器管理固件的开发工作。您可以通过zhouting@cn.ibm.com和她联系。
刘鑫,目前在IBM中国系统技术实验室从事服务器管理固件的开发工作。
刘坚,软件工程师,Linux爱好者,目前在IBM中国软件开发技术实验室从事刀片服务器管理固件的开发工作。您可以通过liujsh@cn.ibm.com和他联系。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: