XSS:跨站脚本攻击
XSS的危害:
- 获取页面数据,偷取网站任意数据
- 获取Cookies,偷取重要用户信息
- 劫持前端逻辑,破坏网站
- 发送请求给其他服务器
- ...等等
造成XSS的原因:
XSS攻击分类:
反射型:一般出现在URL注入和表单输入,经过服务器返回执行
存储型:一般出现在评论之类的需要存储与数据库,然后数据库读取时注入
DOM XSS:与前两者不同的是,它不需要经过服务器,即直接在用户浏览器中输入
XSS攻击注入点,产生的原因,解决办法和相应的实例:
接下来的例子采用node作为服务器,先关闭浏览器的对XSS的默认阻止行为,即设置以下代码
res.set('X-XSS-Protection',0);
1.HTML节点内容
产生的原因:
- 原来的正常数据会生成
<div>
正常数据
</div>
- 那么所注入的脚本就会在浏览器读取到脚本时执行生成
<div>
<script>...</script>
</div>
解决办法: 对做将‘<’和‘>’转义,分别为 < 和 >
str = str.replace(/</g, '<')
str = str.replace(/>/g, '>')
实例:
先是html的表单,和服务器的部分代码
<form action="/upload" method="GET">
<input type="text" name="name">
<input type="submit" value='提交'>
</form>
app.get('/upload', (req, res) => {
let query = req.query.name
res.set('X-XSS-Protection',0); //关闭浏览器的默认阻止XSS行为
res.writeHead(200,{'Content-Type':'text/html;charset=UTF8'})
res.write(query)
res.end()
})
然后在表单输入一串脚本,提交
然后浏览器就会执行脚本的内容
如果对表单数据进行转义,那么就会输出以下结果,而不是执行脚本
app.get('/upload', (req, res) => {
let query = req.query.name
query = query.replace(/</g, '<')
query = query.replace(/>/g, '>')
res.set('X-XSS-Protection',0);
res.writeHead(200,{'Content-Type':'text/html;charset=UTF8'})
res.write(query)
res.end()
})
2.HTML属性
产生原因:
- 正常HTML属性
<img src="正常地址" />
- 属性脚本注入,如下面代码,在src输入一个错误地址,那么就会执行onerror事件,触发后面的脚本
<img src="123" onerror="alert(aaa)" />
解决办法:对双引号,单引号,空格进行转义
" 是 双引号“ 的转义字符,也可以用十进制",单引号用十进制'空格的十进制是 
if (!str) return ''
str = str.replace(/"/g, '"')
str = str.replace(/'/g, ''')
str = str.replace(/ /g, ' ') //如何你的属性值都是带引号的,可以不设
实例:
服务器代码
app.get('/', (req, res) => {
var content = fs.readFileSync('./index.html')
res.writeHead(200,{'Content-Type':'text/html;charset=UTF8'});
res.write(content)
res.end()
})
于是脚本就被执行了
然后进行过滤
app.get('/upload', (req, res) => {
let query = req.query.name
query = query.replace(/"/g, '"')
query = query.replace(/'/g, ''')
res.set('X-XSS-Protection',0);
res.writeHead(200,{'Content-Type':'text/html;charset=UTF8'})
res.write(query)
res.end()
})
就不会执行脚本了 ,只是显示图片找不到
3.JavaScript代码
产生原因:
- 在JavaScript中注入
<script>
var data = "hello"; //正常脚本
var data = "hello";alert(1);""; //被注入的脚本
</script>
解决办法:对“进行 \ 转义,或者直接使用JSON.stringify()对表单进行处理
if (!str) return ''
str = str.replace(/\\/g, '\\\\') //一定要放前面
str = str.replace(/"/g, '\\"')
或者直接
JSON.stringify(form)
4.富文本
产生原因:
- 富文本的内容会被生成HTML形式
解决办法:
使用白名单:
安装: npm install cheerio
引入: var cheerio = require('cheerio')
var $ = cheerio.load(html)
// 白名单
var whiteList = {
'img': ['src'],
'a': ['href']
}
$('*').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)
}
}
})
使用黑名单:
if (!html) return ''
html = html.replace(/<\s*\/?script\s*>/g, '')
html = html.replace(/javascript:[^'"]*/g, '')
html = html.replace(/onerror\s*=\s*['"]?[^'"]*['"]?/g, '')
使用第三方库
安装: npm install xss
var xss = require("xss");
var html = xss('<script>alert("xss");</script>');
5.使用CSP(内容安全策略)
通过指定有效域,即浏览器认可的可执行脚本的有效来源,使服务器管理者有能力减少或消除XSS攻击所依赖的载体
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP
最后:从根源解决,将网站重要的用户信息进行加密,给Cookie设置HttpOnly属性,这样就不能通过document.cookie来访问Cookie了