1.关于XSS攻击
XSS全称Cross Site Scripting(跨站脚本),为了与“CSS”区别,就使用XSS作为简称。
XSS攻击指恶意用户在html中注入含恶意的JavaScript代码或者恶意的HTML代码。在其他用户浏览该页面时,浏览器会直接编译处理所有代码包括恶意代码,从而作出损害用户利益的攻击。
下面通过一个简单的前后端数据交互例子进行叙述,先贴上代码
//页面代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>存储型XSS测试</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
<style type="text/css">
.demo{
width: 450px;
padding: 1.5rem;
margin-right: 0;
margin-left: 0;
}
</style>
</head>
<body>
<div class="container">
<div class="demo">
<div class="d-flex justify-content-around mb-3">
<input type="text" class="form-control mr-3" placeholder="输入添加信息">
<button type="button" class="btn btn-primary flex-shrink-0" onclick="addMessage()">添加</button>
</div>
<div>
<table class="table table-bordered">
<thead>
<tr>
<th>信息</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<script type="text/javascript">
const input=document.querySelector('input')
const tbody=document.querySelector('tbody')
function getMessage(){
fetch('http://127.0.0.1:9500/message',{mode:'cors'})
.then(res=>res.json())
.then(res=>{
let html=res.reduce((cur,el)=>{
cur+=`
<tr>
<td>${el}</td>
</tr>
`
return cur
},'')
tbody.innerHTML=html
})
}
getMessage()
function addMessage(){
fetch('http://127.0.0.1:9500/message',{
mode:'cors',
method:'POST',
body:JSON.stringify({message:input.value}),
headers: {
'content-type': 'application/json'
},
})
.then(()=>getMessage())
}
</script>
</body>
</html>
//后端代码node.js
const express=require('express')
const bodyParser = require("body-parser")
const app=express()
const router=express.Router()
const jsonParser = bodyParser.json()
let message=['John']
router.get('/',function(req,res){
res.status(200).json(message)
})
router.post('/',jsonParser,function(req,res){
message.push(req.body.message)
res.status(200).end()
})
//中间件解决跨域问题
function cors(req,res,next){
res.header('Access-Control-Allow-Origin','*')
res.header('Access-Control-Allow-Headers','Content-Type')
res.header('Access-Control-Allow-Methods','PUT,POST,GET,DELETE,OPTIONS')
next()
}
app.use(cors)
app.use('/message',router)
app.use(function(req,res){
res.status(404).end()
})
app.listen(9500,function(){})
交互效果如下:
此时,如果我们插入带恶意的html片段,如:
<img src="whatever" onerror=alert('attack')>
因为src中是一个无效链接,加载失败必然会执行onerror中的回调函数,因此则会出现以下效果:
这就是其中一种XSS攻击的原理的呈现。一般恶意用户会通过onerror回调函数插入script如下:
<img src="whatever" onerror="javascript:window.el=document.createElement('script'),window.el.src='wrong.js',document.head.appendChild(window.el)">
通过引入来自外部的恶意脚本,在恶意脚本里就可以进行恶意操作,例如:
- 窃取用户Cookie:document.cookie收集用户cookie或者sessionStorage然后发送到别的服务器里
- 监听用户行为:通过登陆的键入字符串窃取用户信息
- 插入恶意广告:插入恶意iframe或者监听鼠标点击跳转页面
**拓展:**有人会问为什么onerror函数中不这么写
javascript:document.head.innerHTML+="<script src='wrong.js'></script>"
那是因为HTML5中指定不执行由innerHTML插入的script标签。
2.XSS攻击的分类
1.存储型XSS攻击
开头的例子就是存储型XSS攻击,存储型的攻击主要步骤是:
- 恶意用户把恶意代码片段提交到服务器的数据库中
- 普通用户请求网站,网站把包含恶意代码的数据加载从页面中加载出来
- 恶意代码开始运行
2.反射型XSS攻击
有些页面交互中存在用户输入数据后,页面响应时会原封不动地返回这个数据到页面上。例如...还是先贴代码吧:
//后端node.js代码
const express=require('express')
const app=express()
app.get('/main/:keyword',function(req,res){
res.send(`搜索内容:${req.params.keyword}`)
})
app.use(function(req,res){
res.status(404).end()
})
app.listen(9501)
正常交互效果:
带XSS攻击的交互效果:
3.DOM型XSS攻击
DOM型XSS攻击与反射型XSS攻击类似,都是通过把恶意代码作为参数纳入到url中,诱发用户点击。不过区别在于反射型XSS攻击的页面是通过后端的模板引擎渲染的,而DOM型XSS攻击中的页面是通过前端取出URL中的参数里的恶意代码后,然后通过document.write或者innerHTML等方法渲染到页面上的。
3.XSS攻击预防措施
总结说,一般XSS攻击中注入的恶意代码有两种类型:
- 在img或者iframe等标签中,后面接着代码,可能以javascript:开头,。
- 以
**拓展:**注意上述两种写法不区分大小写,“javascript:”也可以写成“JaVaScRiPt:”,因为XSS的恶意代码一般都是插入在html标签里,而HTML不区分大小写。
1.关键字转义:
我们可以在后端对"<",">"等符号进行转译成HTML能试别的转义符,如下:
-
”<“转译成“<”
-
“>”转译成“>”
-
引号“"”转译成“"”
转译过程最好在后端进行,如果在前端进行转义,恶意用户可以通过postman等工具直接发起请求绕过前端转义过滤。
**存在缺点:**有时候前端如果需要对数据进行处理,例如统计字符串长度等,要再次把特殊字符转义回原来的格式。而且也不能针对以javascript:开头的恶意代码。
2.长度限制
对于输入的信息,进行长度限制(前后端都要对提交信息进行校验)。一般XSS嵌入的恶意代码无论是那种类型,都需要相当的长度才能实施。尽管有些代码可以拆分,例如开头举例说明的储存型XSS攻击中的恶意img标签:
<img src="whatever" onerror="javascript:window.el=document.createElement('script'),window.el.src='wrong.js',document.head.appendChild(window.el)">
可以把他拆分成三个短的信息:
<img src="whatever" onerror="javascript:window.el=document.createElement('script')">
<img src="whatever" onerror="javascript:window.el.src='wrong.js'">
<img src="whatever" onerror="javascript:document.head.appendChild(window.el)">
甚者可以把代码再拆分成好几段字符串,然后最后拼起来用eval运行。但仍然可以增加XSS攻击的难度。
3.Content Security Policy
CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。---阮一峰的网络日志
我们可以通过两种方法来开启CSP:
(1)后端配置响应头content-security-policy,例如
//中间件配置csp
function csp(req,res,next){
res.header('Content-Security-Policy',"default-src 'self';script-src 'self'")
next()
}
app.use(csp)
发出请求时在响应头里可看到:
(2)在网页的head设置meta标签,如下:
<meta http-equiv="Content-Security-Policy" content="default-src 'self' ;">
在开头的例子里,我们可以如上述设置。把default-src设置为’self‘,代表只能加载自己当前域名下的资源。这样子可以防止开头说的引入外部恶意脚本的XSS的攻击:
<img src="whatever" onerror="javascript:window.el=document.createElement('script'),window.el.src='wrong.js',document.head.appendChild(window.el)">
4.设置cookie的httpOnly属性
若cookie的httpOnly属性为true,则浏览器的cookie不能通过js脚本获取,即不能通过document.cookie获取。这样子可以防止恶意代码获取用户的cookie。