观察如下代码,它们若是做为html的节点内容,就会引发xss攻击javascript
<p><svg onload=prompt(/xss/)></p>
<iframe src="http://www.baidu.com" height="250" width="300"></iframe>
<a href="javascript:alert('xss')">你好</a>
<img src=1 onerror=alert('xss')>
复制代码
所以,XSS 攻击,通常是指攻击者经过在网页中注入恶意脚本,当用户浏览网页时,恶意脚本执行,控制用户浏览器行为的一种攻击方式。html
而后谈谈跨站这个词: 咱们通常但愿网站运行的全部逻辑均来自本站,当本站运行了其余网站的东西,例如常见的js脚本,就可能产生跨站脚本攻击,固然在这里咱们能够理解为不局限于远程脚本,由于一般探测是否页面是否存在潜在的xss攻击,都会使用一个alert框,也被称作xss探测器。举个简单的例子:前端
例子1
// server.js
const express = require('express');
const app = express();
app.get('/', function(req, res){
res.send('hello world')
})
app.get('/index', function(req, res){
const id = req.query.id
res.send(`<textarea>${id}</textarea>`)
})
app.listen(3001)
复制代码
在浏览器输入网址 localhost:3001/index?id=</textarea><script>alert(1)</script>
java
按下回车的那一瞬,并没看到预期的弹框,心里一阵失落,但同时也注意到了textarea输入框并无东西,因而想莫非是谷歌浏览器已经作了什么事情,来预防此种简易的攻击,打开console面板 git
果真,好吧,输给了chrome,我仍是心服口服的,在这里值得提一下,谷歌的安全机制仍是很完善的,这种简单的反射性跨站攻击,谷歌直接就帮忙处理拦截了,safari也会对这种简易的攻击作基本的拦截。 github
好吧,好大一个框! chrome
本质: 插值形成的注入。数据库
脚本程序: <textarea>${id}</textarea>
express
数据源 | 渲染结果 |
---|---|
id: 1 | <textarea>1</textarea> |
id: </textarea><script>alert(1)</script> |
<textarea></textarea><script>alert(1)</script></textarea> |
在这个例子中,攻击者首先把盛放数据的容器提早闭合,而后经过script标签为数据赋予行为,让数据源再也不单纯,改变整个程序的逻辑,从而实现攻击的目的
后端
说到这里,咱们已经用了个简单的例子引出了今天的主题xss,也许有人内心会有疑问,不就是弹个框吗?果然如此吗?咱们能够换个角度考虑一下,咱们平时写的脚本能干什么?
获取页面数据
获取cookie
劫持前端的逻辑
发送请求
...
所以,xss也能够干这些事,只有你想不到,没有黑客作不到的,攻击者经过注入脚本,能够经过dom抓取页面中的数据,也能够经过document.cookie获取用户的cookie,通常cookie保存有用户登录态,攻击者只须要将这个cookie种在本身的浏览器中,就能够获取用户的各类信息,模拟用户进行各类操做等。
上网搜了下以前被xss恶意攻击的案例,发现了一个典型,做为前端工程师,或多或少都知道站酷这个网站,这是国内很是有名的设计师交流平台,在官网首页有个搜索功能,当输入搜索关键词后点击搜索,关键词会在搜索结果页二次显示 站酷
值得说明的是:如今站酷已经修复了,目前的效果是对标签进行了处理
我理解的三者的区别:
经过url连接点击触发,是一次性行为,实质是服务器端没有对用户的恶意输入作安全处理,直接反射相应内容。文章开始的例子1就是一个典型的反射型xss攻击,这类攻击能够经过url辨别,谷歌这类安全性高的浏览器,基本能够自动处理这类攻击
顾名思义,存储,通常涉及后端数据存储,常见的场景就是APP的意见反馈模块,前端经过接口把用户输入的信息传给后端,当有些后台管理系统须要展现反馈意见时,就会取数据库中的这些数据,当这些数据中含有攻击的脚本,那就形成了存储型xss攻击。这种攻击是持久性的。
客户端的脚本程序能够动态地检查和修改页面内容,而不依赖于服务器端的数据。可能引发dom型xss的:使用innerHTML, documen.write属性...
<img src="null" onerror='alert(document.cookie)' />
复制代码
// test.html
<script>
const info = '';alert(1);''
const data = `hello${info}`
</script>
复制代码
X-XSS-Protection: Chrome 和 Safari 的一项功能,可在检测到反射的跨站点脚本(XSS)攻击时阻止页面加载 可选的值:
将标签符号'<', '>'全局转义
...
const escapeHtml = (str) => {
str = str.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '&quto;') .replace(/'/g, ''') .replace(/ /g, ' ') return str } ... app.get('/index', (req, res) => { const id = req.query.id || '' res.send(`<textarea>${escapeHtml(id)}</textarea>`) }) 复制代码
输出:
高度定制要展现的内容,这里我使用了cheerio来将html字符串解析成html实体, cheerio
// <img src="123" onerror="alert(1)"></img>使用cheerio的输出
{ type: 'tag',
name: 'img',
attribs: { src: '123', onerror: 'alert(1)' },
children: [],
next: null,
prev: null,
parent: null,
root:
{ type: 'root',
name: 'root',
attribs: {},
children: [ [Circular] ],
next: null,
prev: null,
parent: null
}
}
复制代码
// server.js 无关代码已省略
...
const whiteList = {
'img': ['src']
}
const xssFilter = (html) => {
if (!html) return ''
const $ = cheerio.load(html, {
normalizeWhitespace: true,
xmlMode: true
})
$('*').each((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)
}
}
})
onsole.log(html, $.html())
return $.html()
}
app.get('/api', (req, res) => {
const cb = (result) => {
result = result.map(item => {
const { comment, name, time } = item
return {
name,
time,
comment: xssFilter(comment)
}
})
res.send({
data: result,
message: 'success',
status: 1
})
}
model.find({}, cb) // 自定义的查询方法
})
...
复制代码
过滤结果: 由于白名单只配置了:'img': ['src'], 所以img的onerror属性被过滤,其余不在白名单的标签总体被过滤
csp是个http的头,实质是用于规定内容是否可执行,所以只要将用户的内容标记为不可执行,就ok了~~~
// server.js
...
app.use((req, res, next) => {
res.set({
'Content-Security-Policy': "default-src 'self'"
})
next()
})
...
复制代码
没有攻击成功