上一篇文章中介绍了XSS,这篇文章介绍CSRFjavascript
Cross-site request forgery, 跨站请求伪造。是指黑客引诱用户打开黑客的网站,在黑客的网站中,利用用户的登陆状态发起跨站请求。html
我本身模拟了一个例子,java
在这个页面内,我会自动向服务器发起一个get请求,服务器响应这个请求的同时会向浏览器发送cookieexpress
在这个页面内,作了三个事情segmentfault
经过截图能够看到,三个请求都带了www.abc.com下的cookie发送给了localhost:8899服务器,而且localhost:8899服务器也正常响应了。
流程图以下:跨域
黑客发起CSRF攻击的条件浏览器
CSRF攻击与XSS攻击不一样,CSRF攻击不会往页面内注入恶意脚本,所以黑客是没法经过CSRF攻击来获取用户页面数据的,因此主要由服务器来作预防。
主要有如下几种方式:安全
SameSite选项一般由Strict、Lax和None三个值服务器
可是如今大部分的网站静态资源都放在单独的域名下,因此经过设置Cookie的SameSite为Strict、Lax是不能正常运行的,因此这个方法只适用静态资源跟服务器接口在同一个站点下的网站。
我测试了一下,以下图:
localhost:8899向www.abc.com写入SameSite值为Lax的cookie,写不进去cookie
locahost:8899向该站点下的localhost:8899/get写入SameSite值为Lax的cookie,成功写入。
在服务器端验证请求的来源站点。由于CSRF攻击大多数都是来自第三方站点。
经过http请求头中的Referer和Origin属性
记录了该http请求的来源地址,但有些场景不适合未来源URL暴露给服务器,因此能够设置不用上传,而且referer属性是能够修改的,因此在服务器端校验referer属性并无那么可靠
经过XMLHttpRequest、Fetch发起的跨站请求或者Post方法发送请求时,都会带上origin,因此服务器能够优先判断Origin属性,再根据实际状况判断是否使用referer判断。
除了上面两个方法以外,还可使用CSRF Token来验证。
代码很简单
<div> <h6>CSRF测试</h2> <a href="http://www.haha.com:9999">点我点我</a> <script> fetch('http://localhost:8899', { method: 'get', mode: 'cors', credentials: 'include' //很重要,容许跨域访问传输cookie }).then((res) => { console.log(res) }) </script> </div>
const express = require('express') const app = express() const port = 8899 //allow custom header and CORS app.all('*',function (req, res, next) { // res.header('Access-Control-Allow-Origin', '*'); // res.header('Access-Control-Allow-Origin', req.hostname); res.header('Access-Control-Allow-Origin', req.headers.origin); res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild'); res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS'); res.header('Access-Control-Allow-Credentials', true); // 很重要,容许跨域访问传输cookie if (req.method == 'OPTIONS') { res.send(200); /让options请求快速返回/ } else { next(); } }); app.get('/', (req, res) => { // res.cookie('name', 'hey', { domain: req.hostname, path: '/'}); res.cookie('name', 'hey', { domain: req.hostname, path: '/', sameSite: 'None'}); // res.cookie('name', 'hey', { secure: true }); res.send('Hello World!a') }) app.get('/get/test', (req, res) => { res.cookie('username', 'hahah', { domain: req.hostname, path: '/', sameSite: 'Lax'}); // res.cookie('name', 'hey', { secure: true }); res.send('Hello World!a') }) app.get('/get', (req, res) => { res.set('Content-Type', 'text/html') console.log(req.headers, 'header') const html = ` <div> <div>服务器同站点下的页面</div> <script> function fetchUrl(url, method='get') { fetch(url, { method, mode: 'cors', credentials: 'include' //很重要,容许跨域访问传输cookie }).then((res) => { console.log(res) }) } fetchUrl('http://localhost:8899/get/test') </script> </div> ` res.send(html) }) app.post('/post', (req, res) => { console.log(req.headers, 'header') // res.cookie('name', 'hey', { secure: true }); res.send('I am post') }) app.listen(port, () => console.log(`Example app listening on port ${port}!`))
const express = require('express') const app = express() const port = 9999 //allow custom header and CORS app.all('*',function (req, res, next) { // res.header('Access-Control-Allow-Origin', '*'); // res.header('Access-Control-Allow-Origin', req.hostname); console.log(req.headers.origin) res.header('Access-Control-Allow-Origin', req.headers.origin); res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild'); res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS'); res.header('Access-Control-Allow-Credentials', true); // 很重要,容许跨域访问传输cookie if (req.method == 'OPTIONS') { res.send(200); /让options请求快速返回/ } else { next(); } }); app.get('/', (req, res) => { res.set('Content-Type', 'text/html') console.log(req.headers, 'header') const html = ` <div> <div>nihao</div> <img src="http://localhost:8899" /> <a href="javascript: fetchUrl('http://localhost:8899/post', 'post');">你好,交个朋友吧</a> <script> function fetchUrl(url, method='get') { fetch(url, { method, mode: 'cors', credentials: 'include' //很重要,容许跨域访问传输cookie }).then((res) => { console.log(res) }) } fetchUrl('http://localhost:8899/post', 'post') </script> </div> ` res.send(html) }) app.listen(port, () => console.log(`Example app listening on port ${port}!`))
以前虽然知道CSRF的原理,可是没有实际模拟过,实际模拟以后,感受,原来真的那么简单。也算是一个小小收获
欢迎跟我一块儿挖坑、填坑。