前端安全的话题再次被说起,大到深航东航系统被攻破,乘客信息泄露并被利用,小到一个word-wrap让服务器暴露信息,各类漏洞和攻击无所不在。在前端江湖中,攻击与防守更像是一场漫无硝烟的战争,悄无声息却无时无刻都在进行。javascript
前端常见漏洞包括XSS、CSRF及界面操做劫持,服务端如SQL注入等。css
前端使用的传输协议是http,不通过加密直接传输的。HTML中有不少地方能够内嵌脚本。及前端的存储,cookie,web storage.这些在必定程度上带来了一些安全风险。html
同域:两个站点同协议,同域名,同端口。前端
出于防范跨站脚本攻击的同源安全策略,浏览器禁止客户端脚本(如Javascript)对不一样域名的服务进行跨域调用。java
同源策略(Same Origin)中的源有着严格的定义,参见RFC6454,第4章节。通常而言,Origin由{protocol, host, port}三部分组成。web
下面是同源检查的一些实例express
可能有点意外的是,通常咱们会认为不一样的子域名应该被当作同域名,是安全的调用,但实际上浏览器同源策略甚至禁止了不一样子域名和端口的服务之间的调用。跨域
跨子域的访问,可以使用iframe,而后设置domain为相同。浏览器
HTTP相关头安全
User-Agent:在使用HTTP协议进行请求时,HTTP协议头部会添加User-Agent,该信息能够标识请求者的一些信息,如什么浏览器类型和版本、操做系统,使用语言等信息。
Referer:HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,通常会带上Referer,告诉服务器我是从哪一个页面连接过来的,服务器籍此能够得到一些信息用于处理。好比从我主页上连接到一个朋友那里,他的服务器就可以从HTTP Referer中统计出天天有多少用户点击我主页上的连接访问他的网站。HTTP Referer使用很是简单,使用场合比较多的是用于页面统计、资源防盗链等,但仍是有一点值得注意的是:Referer是不安全的,客户端能够经过设置改变 Request中的值,尽可能不要用来进行安全验证等方面。
HTML中内嵌脚本执行
可内嵌于js文件中,可出如今HTML的script标签中,HTML标签的事件中,及一些标签的src,href等属性的伪协议javascript:中。这样致使防护XSS有些棘手。使用HTTPOnly cookie能够必定程度防止脚本获取cookie。
AJAX
AJAX的请求头设置是有限制的。AJAX严格遵照同源策略。
XSS攻击
XSS是一种常常出如今web应用中的安全漏洞,它容许恶意web用户将代码植入到提供给其它用户使用的页面中。好比这些代码包括HTML代码和客户端脚本。攻击者利用XSS漏洞旁路掉访问控制——例如同源策略(same origin policy)。首先,攻击者向web页面注入恶意代码,而后,这段代码被浏览器执行,这样,一次攻击就成功了。
分类
1.XSS反射型攻击,恶意代码并无保存在目标网站,经过引诱用户点击一个连接到目标网站的恶意连接来实施攻击的。
2.XSS存储型攻击,恶意代码被保存到目标网站的服务器中,这种攻击具备较强的稳定性和持久性,比较常见场景是在博客,论坛等社交网站上,但OA系统,和CRM系统上也能看到它身影,好比:某CRM系统的客户投诉功能上存在XSS存储型漏洞,黑客提交了恶意攻击代码,当系统管理员查看投诉信息时恶意代码执行,窃取了客户的资料,然而管理员绝不知情,这就是典型的XSS存储型攻击。
一个简单的例子,在发帖子的时候,嵌入
<script>alert('XSS')</script>
若是在帖子内容里出现了这样的语句,被浏览器执行了,一次XSS攻击就成功了。 要避免此类攻击,通常会对用户输入采起过滤,最多见的就是对<>转换成<以及>,通过转换之后<>虽然可在正确显示在页面上,可是已经不能构成代码语句了。这个貌似很完全,由于一旦<>被转换掉,什么<script src=1.js></script>就会转换成“<script src=1.js></script>”,不能执行,所以,不少人认为只要用户的输入没有构成<>,就不能闭合先后的标签,其语句固然也不会有害,可是,万事总有可能,只要有必定的条件,咱们就能够构造通过编码后的语句来进行XSS。
再好比
<img src='javascript:alert("XSS")'>
这类攻击须要过滤用户输入的src属性,过滤掉里边的javascript开头的关键字,封堵XSS。另外对于各类可能绕过过滤的状况,封堵XSS漏洞。
不少时候,产生XSS的地方会有变量的长度限制,这个限制多是服务器端逻辑形成的。假设下面代码存在一个XSS漏洞:
<input type=text value="$var" />
服务器端对输出变量$var作了严格的长度限制,那么攻击者可能会这样构造XSS:
但愿达到的输出效果是:
<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>" />
中间的代码前部被<!-- -->注释掉了。
XSS的防护:
1)HttpOnly:浏览器将禁止页面的Javascript访问带有HttpOnly属性的Cookie。是为了解决劫持Cookie攻击。由于Javascript获取不到Cookie的值。
C#中设置HttpOnly的方法:
HttpCookie
myCookie=new HttpCookie("myCookie");
myCookie.HttpOnly=true;
Response.AppendCookie(myCookie);
2)输入检查:
常见的Web漏洞如XSS、SQL诸如等,都要求攻击者构造一些特殊字符,这些特殊字符多是正经常使用户不会用到的,因此输入检查就有存在的必要了。
例如,用户名可能会被要求只能为字母、数字的组合。
输入检查的逻辑应该放在服务器端,不然很容易被绕过。目前的广泛作法是在客户端和服务器端都执行检查。
在XSS的防护上,输入检查通常是检查用户输入的数据中是否包含一些特殊字符,如< > ' "等。若是发现,则将这些字符过滤掉或编码。
比较智能的还会检查<script> javascript等敏感字符,网上有不少XSS Filter资源。但由于XSS Filter对语境不了解,有时不能检查出XSS攻击。
3)输出检查:
通常来讲,除了富文本的输出外,在变量输出到HTML页面时,可使用编码或者转移的方式来防护XSS攻击。
1)安全的编码函数:
针对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;
如此代码能够保证是安全的。
2)只需一种编码吗?
XSS攻击主要发生在MVC架构中的View层。大部分的XSS漏洞能够在模版系统中解决。
不是使用了auto-escape就万事大吉了,XSS的防护须要区分状况对待。
4)正确地防护XSS
XSS的本质仍是一种"HTML注入”。
想要根治XSS,能够列出全部XSS可能发生的场景,在一一解决。
a)在HTML标签中输出:如
<div>$var</div>
<a href=# >$var</a>
这样能够构造出:
<div><script>alert(/xss/)</script></div><a href=#><img scr=# onerror=alert(1) /></a>
防护方法是对变量使用HtmlEncode。
b)在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")];
这种严格的编码方式,能够保证不会出现任何安全问题。
c)在<script>标签中输出:如
<script>
var x="$var";
</script>
能够构造出:
<script>
var x="";alert(/xss/);//";
</script>
防护时也是使用JavascriptEncode
d)在事件中输出:如
<a href=# onclick="funcA('$var')" >test</a>
能够构造出:
<a href=# onclick="funcA('');alert(/xss/);//')" >test</a>
在防护时须要使用JavascriptEncode
e)在CSS中输出:在CSS和style、style attribute中造成的XSS的方式很是多样化,参考下面几个XSS的例子。
<STYLE>@import 'http://ha.ckers.org/xss.css';</STYLE>
<STYLE>BODY
{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}</STYLE>
<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()函数。
f)在地址中输出:
在地址中输出也较为复杂。通常来讲,在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.comsearch="?abc=123"
hash=#ssss
在Protocol和host中,若是使用严格的URLEncode函数,则会把"://"、"."等都编码掉。
对于以下的输出方式:
<a href="http://supershll.blog.163.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
5)处理富文本:
有些时候,网站须要容许用户提交一些自定义的HTML代码,称之为"富文本"。好比,一个用户在论坛里发帖,帖子里的内容里要有图片、视频、表格等,这些“富文本”的效果都须要经过HTML代码来实现。
如何区分安全的“富文本"和有攻击性的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对样式进行智能分析,检查其中是否包含危险代码。
6)防护DOM Based XSS:
DOM Based XSS是一种比较特别的XSS漏洞,前文提到的几种防护方法都不太适用,须要特别对待。
DOM Based XSS是如何造成的呢?回头看看这个例子:
<script>
function test(){
var str=document.getElementById("text").value;
document.getElementById("t").innerHTML="<a href='http://supershll.blog.163.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://supershll.blog.163.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://supershll.blog.163.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("<a href='http://supershll.blog.163.com/blog/”+x+"' >test</a>");
</script>
页面渲染以后的实际结果以下:
XSS攻击成功。
其缘由在于,第一次执行JavascriptEscape后,只保护了:
var x="$var";
可是当document.write输出数据到HTML页面时,浏览器从新渲染了页面。在<script>标签执行时,已经对变量x进行了解码,其后document.write再运行时,其参数就变成了:
<a href='http://supershll.blog.163.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返回的数据
7)
换个角度看XSS的风险
从业务角度看XSS风险,用户之间有互动的页面的风险确定比没有互动的页面的风险高。应重点修补风险高的页面。
理论上,XSS漏洞虽然复杂,但倒是能够完全解决的。在设计XSS解决方案时,应该深刻理解XSS攻击的原理,针对不一样的场景使用不一样的方法。
CSRF攻击
CSRF是Cross Site Request Forgery的缩写(也缩写为XSRF),直译过来就是跨站请求伪造的意思,也就是在用户会话下对某个CGI作一些GET/POST的事情——这些事情用
户未必知道和愿意作,你能够把它想作HTTP会话劫持。
防护:使用验证码,加密等方法防护。请求Referer验证。cookie设置为HTTPOnly,防止被窃取。