1。如果 web 服务器在返回的 HTTP Content-Type 响应头中,没有明确设置字符集编码,并且响应体中的 HTML 文档中的 meta 元素内,也没有设置字符集编码,那么浏览器在碰到任何 UTF-7 字符时,都会尝试确定其编码方案,并且解码还原成明文字符,以上述文档为例,就会弹出提示框;
2。如果 web 服务器在返回的 HTTP Content-Type 响应头中,明确设置字符集编码为 UTF-8,并且响应体中的 HTML 文档中的 meta 元素内设置的字符编码为 UTF-7,那么浏览器最终将按照响应头中的设置(响应头的优先级比响应体中 meta 标签的优先级高 ),以上述文档为例,不会弹出提示框(因为采用 UTF-8 字符编码时,浏览器无法识别,解码 +ADW- 与 +AD4- 字符);
3。如果 web 服务器在返回的 HTTP Content-Type 响应头中,设置字符集编码为 UTF-7,并且响应体中的 HTML 文档中的 meta 元素内设置的字符编码为 UTF-8,
根据浏览器的采用优先级原则,上述文档将会弹出提示框;
总结,应对 UTF-7 XSS攻击,从最终用户防御的角度来看,应该确保浏览器总是处在当前的最新版本状态;从 web 站点防御的角度来看,应该明确在响应头与响应体的 HTML 文档中,设置字符编码为 UTF-8 或者其它安全的编码方案;
并且过滤掉任何 UTF-7 危险字符,如果没有把握通过编程屏蔽所有危害字符,则可以模仿浏览器的 HTML 解析引擎将用户输入的内容在内存中保留一份副本,将其渲染成 HTML 页面,在渲染结果中查找任何明文的危害字符,对其进行编码过滤,然后再次渲染,再过滤,直到完全清除干净后,才可以把最终的文档返回给客户端。
由于各种浏览器对于新的 HTML5 规范,以及 DOM 的支持,实现程度不一致,为了保证自己所编写的文档,以及其中的脚本代码能够在所有主流浏览器中正确运行,一种解决办法是:先判断浏览器类型,然后执行相应浏览器支持的代码块。实现这个功能的关键,在于浏览器的用户代理字串值,即 navigator.userAgent
实现逻辑如下:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>XssPayloadTest</title>
</head>
<body>
<script>
document.write(navigator.userAgent);
document.write("<br><br>");
var isIE = navigator.userAgent.indexOf('Trident') > 0;
var isChrome = navigator.userAgent.indexOf('Chrome') > 0;
var isFirefox = navigator.userAgent.indexOf('Firefox') > 0;
var isOpera = navigator.userAgent.indexOf('OPR') > 0;
if (isIE){
document.write('<b>您使用的是IE浏览器</b>');
}else if (isChrome){
if (isOpera){
document.write('<b>您使用的是Opera浏览器</b>');
}else{
document.write('<b>您使用的是Chrome浏览器</b>');
}
}else if (isFirefox){
document.write('<b>您使用的是Firefox浏览器</b>');
}else{
document.write('<b>您使用的是其它浏览器</b>');
}
</script>
</body>
</html>
这是一个非常简单的先判断然后输出用户使用的浏览器类型的页面。
第8行直接在当前页面输出打开该页面的浏览器的用户代理字串;第11~14行分别查找各种浏览器的用户代理字串中,能够唯一识别浏览器类型的关键词,这是由于,在早期的“浏览器世界大战”中,许多 web 页面的特定内容仅支持 Netscape Navigator 浏览器(网景公司的产品,其 “Mozilla” 字串就是它首创的,含义为“Mosaic Killer”,即浏览器鼻祖 Mosaic 的杀手),而其它品牌浏览器为了提高自身市场份额与竞争力,也在其用户代理中添加了“Mozilla”字串,用于告诉目标站点:“我是与 Mozilla 兼容的浏览器”,如此一来,站点就会返回原本仅响应给 Mozilla 浏览器的页面内容,而这个内容通常是比较丰富的;