DOM-XSS攻击原理与防御
⽬录
XSS的中⽂名称叫跨站脚本,是WEB漏洞中⽐较常见的⼀种,特点就是可以将恶意HTML/JavaScript代码注⼊到受害⽤户浏览的⽹页上,从⽽达到劫持⽤户会话的⽬的。XSS根据恶意脚本的传递⽅式可以分为3种,分别为反射型、存储型、DOM型,前⾯两种恶意脚本都会经过服务器端然后返回给客户端,相对DOM型来说⽐较好检测与防御,⽽DOM型不⽤将恶意脚本传输到服务器在返回客户端,这就是DOM型和反射、存储型的区别,所以我这⾥就单独的谈⼀下DOM型XSS。
#
DOM⽂档
为了更好的理解DOM型XSS,先了解⼀下DOM,毕竟DOM型XSS就是基于DOM⽂档对象模型的。对于浏览器来说,DOM⽂档就是⼀份XML⽂档,当有了这个标准的技术之后,通过JavaScript就可以轻松的访问它们了。
下⾯举例⼀个DOM将HTML代码转化成树状结构:
<html>
<head>
<meta charset="gbk" />
<title> TEST </title>
</head>
<body>
<p>The is p.<p>
<h1>Product:</h1>
<ul>
<li>Apple</li>
<li>Pear</li>
<li>Corn</li>
</ul>
</body>
</html>
转化成模型如下图:
这样做的好处就是,通过这种简单的树状结构,就能把元素之间的关系简单明晰的表⽰出来,⽅便客户端的JavaScript 脚本通过DOM 动态的检查和修改页⾯内容,不依赖服务端的数据。
利⽤原理
客户端JavaScript 可以访问浏览器的DOM ⽂本对象模型是利⽤的前提,当确认客户端代码中有DOM 型XSS 漏洞时,并且能诱使(钓鱼)⼀名⽤户访问⾃⼰构造的URL ,就说明可以在受害者的客户端注⼊恶意脚本。利⽤步骤和反射型很类似,但是唯⼀的区别就是,构造的URL 参数不⽤发送到服务器端,可以达到绕过WAF 、躲避服务端的检测效果。为了更⽅便⼤家的理解,下⾯我举⼏个场景给⼤家理解。
场景⼀:innerHTML <html>
<head>
<title> DOM-XSS TEST </title>
<style>
#box{width:250px;height:200px;border:1px solid #e5e5e5;background:#f1f1f1;}
</style>
</head>
<body>
<script>
var ElementById("box");
var ElementById("span1");
##
var ElementById("text1");
var ElementById("Btn");
oBox.innerHTML = oBox.innerHTML + oSpan.innerHTML + oText.value + "<br/>";
// oBox.innerHTML += oSpan.innerHTML + oText.value + "<br/>";//这是简便的写法,在js 中 a=a+b ,那么也等同于 a+=b oText.value=""
}; }
</script> <div id="box"></div>
<span id="span1">⼩明:</span>
<input type="text" id="text1"/>
<input id="Btn" type="button" value="发送消息" name=""/>
</body>
</html>
第⼀次是正常访问:
hellow
第⼆次是将JavaScript 代码作为参数写⼊值中:
<svg/onload=alert(1)>
这⾥我只在⽕狐浏览器利⽤成功,在chrome 利⽤失败,我猜可能chrome 对安全防护做得⽐较好,这⾥不继续各个浏览器版本问题。
使⽤innerHTML 、outerHTML 时要注意,标签需不进⾏编码处理,可能会导致XSS 。防护⽅法就是替换成innerText ,它⾃动将HTML 标签解析为普通⽂本,所以HTML 标签不会被执⾏,避免XSS 攻击。
oBox.innerText = oBox.innerHTML + oSpan.innerHTML + oText.value + "<br/>";
场景⼆:跳转
<html>
<head>
<title> DOM-XSS TEST </title>
</head>
<body>
#
<script>
var hash = location.hash; if(hash){
var url = hash.substring(1);
location.href = url;
}
</script>
</body>
</html>
正常访问是⽤#去实现页⾯跳转,但是因为跳转部分参数可控,可能导致Dom xss 。
通过 location.hash 的⽅式,将参数写在 # 号后,既能让JS 读取到该参数,⼜不让该参数传⼊到服务器,从⽽避免了WAF 的检测。变量hash 作为可控部分,并带⼊url 中,变量hash 控制的是#之后的部分,可以使⽤伪协议#javascript:alert(1)。常见的⼏种伪协议有javascript:、
vbscript:、data:等。⽽现在的移动端(android 和ios
),都可以⾃定义这种协议从浏览器打开本地app ,具体可以看看wwwblogs/WuXiaolong/p/8735226.html
#javascript:alert(1)
场景三:eval
#';alert(1);//
直接将⽤户输⼊数据拼接到代码⾥。
eval("var x = '" + location.hash + "'");
场景四:cookie 、referrer
从localStorage 、SessionStorage 、Cookies 储存源中取数据,这些值往往会被开发者忽略,认为这些值都是在浏览器获取的,是安全的,就未进⾏处理。
var cookies = kie;
document.write(cookies);
场景五:document.write 、document.URL.indexOf("id=")
var ids = document.URL;
document.writeln(ids.substring(ids.indexOf("id=")+3,ids.length));
indexOf 获取url ⾥⾯的参数,然后通过writeln( )或者write( )输出到HTML ,造成xss ,不过我现在(2020.3)在chrome 和firefox 浏览器测试,write()函数很难利⽤,除⾮结合⼀些特殊场景。
防护策略
还有⼀些正则匹配缺陷、业务逻辑型缺陷、配合移动端跳转等、使⽤第三⽅前端框架(⽐如多媒体编辑框)等场景没有⼀⼀进⾏说明(精⼒实在有限了...),后期有空可能会继续补全这些场景。####
检测的流程就是通过查看代码是否有document.write、eval、window之类能造成危害的地⽅,然后通过回溯变量和函数的调⽤过程,查看⽤户是否能控制输⼊。如果能控制输⼊,就看看是否能复现,能复现就说明存在DOM XSS,需要对输⼊的数据进⾏编码。
代码审计时审计的特征点(包括但不限于):
var elements = location.hash;
elements.indexOf
var ElementById("Btn");
oBtn.innerHTML
oBtn.outerHTML
oBtn.setAttribute
oBtn.appendChild
document.write
document.writeln
eval("var x = '" + location.hash + "'");
setTimeout("alert('xss')", 1000)
window.setTimeout
document.setTimeout
window.setInterval
js语法很灵活、库函数也很多,这⾥没法完全列举全,我觉得需要对js语法体系有⼀定了解,才可能更多的全这些特征。
当业务需要必须得将⽤户输⼊的数据放⼊html,那就要尽量使⽤安全的⽅法,⽐如innerText(),testContent()等。在使⽤框架时尽量使⽤框架⾃带的安全函数。
#
参考⽂档
《XSS跨站脚本-攻击剖析与防御》
《Web漏洞防护》
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论