web安全 - XSS攻击与防御
xss(Cross-Site Scripting), 跨站脚本攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。
利用恶意脚本攻击者可以获取用户的敏感信息,Cookie, SessionID等,进而危害数据安全。
1. xss 类型
1. 存储型xss: 恶意脚本来源于数据库
由于恶意代码存储在服务器的数据库中具有持久性和稳定性。
- 攻击步骤
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在HTML中返回给浏览器执行。
- 用户浏览器接收到响应后解析执行,拼接在其中的恶意代码也同时被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行恶意操作。
2. 反射型xss: 恶意脚本来源于受害者的主动请求的URL
攻击者往往通过构造恶意URL链接,诱导用户点击,被攻击网站则将恶意代码返回到浏览器从而执行。
- 攻击步骤
- 攻击者构造出特殊的URL,其中包含恶意代码。
- 用户打开带有恶意代码的URL时,网站服务端将恶意代码从URL中取出,拼接在HTML中返回给浏览器。
- 用户浏览器接收到响应后执行,混在其中的恶意代码也同时被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行恶意操作。
3. Dom xss: 由于前端代码通过修改Dom节点导致恶意代码的执行
2. xss漏洞利用
- cookie 劫持
- 构造GET与POST请求
- xss钓鱼
- 识别用户浏览器
- 识别用户安装的软件
- 识别用户访问过的链接
- 获取用户的真实IP地址
- xss 蠕虫
3. xss 防御
- 服务端设置Set-Cookie 时添加HttpOnly, 从而禁止客户端脚本读取cookie,防范cookie被劫持。
- 输入检查,设置输入白名单,禁止输入恶意危险字符。 (必须在服务端进行输入检查,
客户端输入检查容易被绕过) - 输出编码和转义,结合具体的输出上下文环境利用合适的编码函数进行输出前的编码和转义,
从而防止恶意代码被执行。对于存储型xss 和反射性xss在服务端输出时进行编码,对于Dom 型xss
在客户端进行编码。
4. 正确地防御xss
xss 本质是一种"html注入", 用户的数据被当成了HTML代码一部分来执行,从而混淆了原本的语义。
- 在HTML标签中输出时 (HTMLEncode)
<div>$var</div>
<a href="#">$var</a>
构造xss如下
<div><script>alert('xss')</script></div>
<a href="#"><img src="#" onerror="alert(1)" /></a>
- 在HTML属性中输出 (HTMLEncode)
<div id="abc" name="$var" ></div>
构造xss:
<div id="abc" name=" "><script>alert(/xss/)<script><" ">/div>
- 在
<script>
标签中输出 (JavascriptEncode)
<script>
var x = "$var";
</script>
构造xss:
<script>
var x = "";alert(/xss/);//";
</script>
- 在事件中输出 (JavascriptEncode)
<a href="#" onclick="funcA('$var')">test</a>
构造xss:
<a href="#" onclick="funcA(' ');alert(/xss/);//')" >test</a>
- 在css中输出 (ESAPI.encoder().encodeForCSS())
<STYLE>@import 'http://ha.cker.org/xsscss';</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>
<DIV STYLE="background-image: url(javascript:alert('XSS'))">
<DIV STYLE="width: expression(alert('XSS'));">
由于在css中形成xss的多样化,尽量避免在Style标签,HTML标签的style 属性, 以及CSS文件中输出变量。
- 在地址中输出 (URLEncode)
URLEncode 会将字符转为"%HH"形式,主要是对url中的path 和 search 进行编码,Protocal 和 Host部分
则不需要进行编码
<a href="http://www.evil.com/?test=$var"> test</a>
构造xss:
<a href="http://www.evil.com/?test="onclick=alert(1)""> test</a>
<a href="$var">test</a>
构造xss:
<a href="javascript:alert(1);">test</a>
<a href="data:text/html;bsae64,PHNJcmlwdD5hbGVydCgxKTs8L3NjcmlwdD4=">test</a>
对于可能出现伪协议的场景,先判断变量是否以http开头,否则自动添加,然后在对于余下的path和
search部分进行URLEncode
5. 处理富文本
对于富文本,进行输入检查,使用白名单,只允许相对安全的标签,a, img, div等。
最佳实践就是使用xss filter 进行检查和过滤。
6. 防御Dom 型xss
Dom型xss 防御也需要在合适场景选用合适的编码方案。最关键的是要关注和防范它的输入点:
- javascript 输出HTML到页面
document.write()
document.writeln()
xxx.innerHTML = "<div></div>"
xxx.outerHTML = "<div></div>"
innerHTML.replace
document.attachEvent()
window.attachEvent()
document.location.replace()
document.location.assign()
...
- 页面相关元素输入
页面所有的inputs输入筐
window.location(href, hash等)
window.name
document.referrer
document.cookie
localstorage
XMLHttpRequest返回的数据