XSS介绍
关键字:参数必须传递到后端、参数必须可控、参数必须被浏览器解析
跨站脚本攻击指的是自己的网站运行了别的网站里面的代码
攻击原理是原本需要接受数据但是一段脚本放置在了数据中:
该攻击方式能做什么?
- 获取页面数据
- 获取Cookies
- 劫持前端逻辑
- 发送请求到攻击者自己的网站实现资料的盗取
- 偷取网站任意数据
- 偷取用户密码和登陆状态
- 改变按钮的逻辑
XSS攻击类型
其实XSS的种类非常的多尤其是变种的特别多,大致可以分为两种
1. 反射型:是通过URL参数直接注入,一般是使用alert来探测站点是否防御,直接攻击的使用src来引入自己的脚本
关注从用户可控终端(一般是指前端用户输入)传入后端的参数且返回给浏览器被解析。
http://localhost:1521/?from=<script>alert(1)</script>bing
常见获取前端参数方式:
1)、场景1:使用Servlet技术:request.getParameter(“name”)
2)、场景2:使用Struts2框架:
(1)通过Action类绑定参数或model获取前端的参数
(2)通过Action的属性接收参数:
3)、场景3:使用SpringMVC获取前端参数:
(1)对象方式(类似于struts2的对象绑定方式)
(2)自定义方法参数名(使用注解参数绑定的形式)
(3)使用requestbody绑定注解获取参数
(4)使用request获取参数值
4)、如何审计
1、在前端页面测试1文本框中输入,提交到后端
2、进入后端发现未对来自前端的参数处理且直接反馈给前端,前端在右上图中直接渲染触发反射性XSS漏洞
3、同理在测试2Input框中输入alert(1),提交后端发现对提交到后端发现对特殊字符进行过滤且进行防大小写过滤处理
4、在测试4中input框中提交提交到后端使用ESAPI,发现已经被编码处理
2.存储型:存储到DB后读取时注入(危害很大)
存储型XSS又称持久型XSS,攻击脚本存储在目标服务器的数据库中,具有更强的隐蔽性。
攻击者在论坛、博客、留言板中,发帖的过程中嵌入XSS攻击代码,帖子被目标服务器存储在数据库中。当用户进行正常访问时,触发XSS代码。
如何审计:
1、查找从数据库查询数据并且显示在前端的爆发点
2、通过代码可得将用户要修改的数据显示在前端(去确定能够成功修改成js脚本)
3、提交数据进入后端追踪数据库确定非法参数是否会被过滤,调用以下方法修改用户信息,在最后一行进入service执行修改业务逻辑
4、同样在业务层处理完其他的业务后依旧调用了dao的修改用户信息的sql操作方法
5、调用mapping.xml利用mybatis执行了数据库的update操作,到此发现未进行安全处理,基本确定存在存储型XSS漏洞
6、到数据库里边去确定我们的非法字符是否成功插入到库中
7、最后需要看是否能触发(不能触发不代表不是漏洞,不能触发是前端的标签没有闭合导致浏览器解析不成功)
(1)用户名称位置上的JS脚本被成功解析(未编码处理)
(2)进入到详情页浏览器不能解析该字段的JS脚本(原因可能是闭合不成功导致浏览器不能解析)
(3)想法构造闭合使其浏览器解析JS脚本
(4)按照特征闭合后依然不能触发,发现此详情页面已经被编码处理无法构造(Textarea标签自带转义功能)
(5)之前用户列表用户名字段中JS能被解析是因为
(6)点击修改用户页面只有重新构造用户名字段(必须成功闭合),点击保存提交,同样发现会触发XSS
XSS攻击注入点:
html节点内容:如果一个节点是动态生成的,有可能这个节点的数据有脚本(用户输入信息)
html属性:某个html的属性是由用户输入的,输入的内容可能有脚本
<img src="1" onerror="alert(1)"/>
1" onerror="alert(1) // src被提前关闭
js代码:js代码中存在后台注入的变量或者用户输入的信息
localhost:1521/?from=google";alert(1);"
富文本:其实是一大段的html,我们需要保留格式又要去掉script标签,这是比较麻烦的
富文本得保留HTML,HTML有XSS就有攻击风险
实际上浏览器有着XSS的部分防御机制,可以通过
ctx.set('X-XSS-Protection',0); // 0-disable 1-enable
来进行关闭,浏览器的防御很有限,只能是反射型的参数并且出现在html节点和属性中才会进行防御,在js和富文本中是不会拦截的。
五种防御方式
1. HTML节点内容的XSS防御
转义掉<<和>> 即转义掉<>即可,转义的时机有两种,一种是写入数据库的时候进行转义,另一种实在解析的时候进行转义。
这里是在显示的时候转义
var escapeHtml = function(str){
str = str.replace(/>/g, '<');
str = str.replace(/>/g, '>');
return str;
}
escapeHtml(content);
2. HTML属性的XSS防御
转义”&quto; 即转义掉双引号,'转义掉单引号,(另一个要注意的是实际上html的属性可以不包括引号,因此严格的说我们还需要对空格进行转义,但是这样会导致渲染的时候空格数不对,因此我们不转义空格,然后再写html属性的时候全部带上引号)这样属性就不会被提前关闭了
var escapeHtmlProperty = function(str){
str = str.replace(/"/g, '&quto;');
str = str.replace(/'/g, ''');
str = str.replace(/ /g, ' ');
return str;
}
escapeHtml(content);
其实以上这两个函数可以合并成一个函数,这样不管是内容还是属性都可以使用一个函数来过滤了:
3.HTML转义函数
var escapeHtmlProperty = function(str){
if(!str) return '';
str = str.replace(/&/g, '&');
str = str.replace(/>/g, '<');
str = str.replace(/>/g, '>');
str = str.replace(/"/g, '&quto;');
str = str.replace(/'/g, ''');
return str;
}
escapeHtml(content);
4. js转义
转义”\”或者替换成json
var escapeForJs = function(str){
if(!str) return '';
str = str.replace(/\\/g,'\\\\');
str = str.replace(/"/g,'\\"');
}
这里的解决方式并不完整,因为还有可能是单引号或者其他形势包裹的,这里最保险的方法其实很简单,就是对数据做一次JSON.stringify即可
JSON.stringify()的作用是将 JavaScript 对象转换为 JSON 字符串
5.富文本
由于需要完整的HTML因此不太容易过滤,一般是按照白名单进行保留部分标签和属性来进行过滤,除了允许的标签和属性,其他的全部不允许(也有黑名单的方式,但是由于html复杂效果比较差,原理就是之前的正则替换)
其实可以用别人写好的XSS组件就叫做xss,直接安装引用
npm install xss
白名单-使用第三方库XSS,支持指定白名单
var xssFilter = function(html){
if(!html) return '';
var xss = require('xss');
var ret = xss(html, {
whiteList:{
img: ['src'],
a: ['href'],
font: ['size', 'color']
},
onIgnoreTag: function(){
return '';
}
});
console.log(html, ret);
return ret;
};
漏洞修复
1、配置过滤器之后,编辑用户信息
2、查看输入的用户名称是否被过滤处理,发现用户名称已经被过滤处理
(1)进入后端发现该字段已经被处理
(2)从数据库查看插入到数据库的字段已经被过滤
(3)最后进入前端查看确实用户名称已经被安全编码,从而避免了触发XSS漏洞
修复方法总结:一般通过XSS全局过滤器进行对request中的所有参数拦截处理,对用户输入的内容进行非法字符的判断,如果还有非法字符,例如’、”、<、>等编码转义处理,使其浏览器不能解析此类字符,在java中一般采用的封装了ESAPI的XSS的Filter处理。
小结下XSS防御方法:
1、不要在允许位置插入不可信数据。
2、在向HTML元素内容插入不可信数据前对HTML解码。
3、在向HTML常见属性插入不可信数据前进行属性解码。
4、在向HTML JavaScript Data Values插入不可信数据前,进行JavaScript解码。
5、在向HTML 样式属性值插入不可信数据前,进行CSS解码。
6、在向HTML URL属性插入不可信数据前,进行URL解码。