XSS
- 基本概念
- 攻击原理
- 攻击类型
- 预防措施
- 实践
一、基本概念
跨站脚本攻击
二、攻击原理
通过注入可执行的代码且被浏览器执行,这就产生了执行成功的两个必要条件:
- 注入恶意代码
- 恶意代码被成功执行
三、攻击类型
- 反射型,发出请求时,XSS代码出现在URL中,作为输入提交到服务器,服务器解析并响应,XSS代码随着响应内容返回浏览器,浏览器执行XSS代码。
使用express搭个服务
var express = require('express')
var app = express()
app.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:8082');
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Methods', '*');
res.header('Content-Type', 'application/json;charset=utf-8');
next();
})
app.get('/test', function (req, res, next) {
// 2. 服务端解析成JSON后响应
res.json({
test: req.query.test
})
})
app.listen(8081, () => console.log('Example app listening on port 8081!'))
使用http-server起一个8082,html内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<textarea name="txt" id="txt" cols="100" rows="8"></textarea>
<button type="button" id="test">测试</button>
<img src="null" onerror='alert(document.cookie)' />
<script>
var test = document.querySelector('#test')
test.addEventListener('click', function () {
var url = `http://localhost:8081/test?test=${txt.value}` // 1. 发送一个GET请求
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
// 3. 客户端解析JSON,并执行
var str = JSON.parse(xhr.responseText).test
var node = `${str}`
document.body.insertAdjacentHTML('beforeend', node)
} else {
console.log('error', xhr.responseText)
}
}
}
xhr.open('GET', url, true)
xhr.send(null)
}, false)
</script>
</body>
</html>
在textarea中输入<img src="null" onerror='alert(1)' />
,将会弹出1。以上模拟了一次反射型的xss攻击.
- 储存型,与反射型的区别在于,提交的代码会存储在服务器上(数据库,内存,文件系统)。具有较强的稳定性和持久性。
eg:用户a在留言板留言,里面插入了恶意代码并提交。网站将留言存在数据库中。用户b访问留言板,用户a的恶意代码此时被浏览器执行。此时用户a可能窃取了用户b的信息,或者将用户b的访问重定向到钓鱼网站等等
四、预防措施
- 编码,对用户输入的数据进行HTML Entity编码。把 &, <, >, 空格, ', ", 换行符 等改为 &, <, >, , ', "转义字符或十进制字符
- 过滤,移除用户上传的DOM属性(如onerror等)和节点(style、script、iframe等)
- 校正,避免直接对HTML Entity解码;使用DOM Parse转换,校正不匹配的DOM标签
五、实践 建议采用目前主流成熟的库xss,不要自己写
- 前端使用
<script src="https://rawgit.com/leizongmin/js-xss/master/dist/xss.js"></script>
<script>
// apply function filterXSS in the same way
var html = filterXSS('<script>alert("xss");</scr' + 'ipt>');
alert(html);
</script>
- 后端使用
var express = require('express');
var router = express.Router();
router.get('/test', function(req, res, next) {
res.send(xss(req.body.userInput))
});
module.exports = router;
注:xss在使用时如果过滤的是一段html,那么它默认会过滤掉标签上的style,这里在富文本使用时不友好。可以按照以下配置
// 第一个参数是需要过滤的字符串
xss(text, {
// 自定义匹配标签属性的处理程序功能, 参数依次为:标签名(<a></a>为a),属性名称,属性值,是否在白名单中
onTagAttr: function(tag, name, value, isWhiteAttr) {
if (name === 'style') {
return `${name}='${value}'`
}
}
})