92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线

Web安全之CSRF實例解析

2020-5-11    seo達人

前言

文章首次發表在 個人博客


之前寫過一篇 web安全之XSS實例解析,是通過舉的幾個簡單例子講解的,同樣通過簡單得例子來理解和學習CSRF,有小伙伴問實際開發中有沒有遇到過XSS和CSRF,答案是有遇到過,不過被測試同學發現了,還有安全掃描發現了可能的問題,這兩篇文章就是簡化了一下當時實際遇到的問題。


CSRF

跨站請求偽造(Cross Site Request Forgery),是指黑客誘導用戶打開黑客的網站,在黑客的網站中,利用用戶的登陸狀態發起的跨站請求。CSRF攻擊就是利用了用戶的登陸狀態,并通過第三方的站點來做一個壞事。


要完成一次CSRF攻擊,受害者依次完成兩個步驟:


登錄受信任網站A,并在本地生成Cookie

在不登出A的情況,訪問危險網站B

CSRF攻擊


在a.com登陸后種下cookie, 然后有個支付的頁面,支付頁面有個誘導點擊的按鈕或者圖片,第三方網站域名為 b.com,中的頁面請求 a.com的接口,b.com 其實拿不到cookie,請求 a.com會把Cookie自動帶上(因為Cookie種在 a.com域下)。這就是為什么在服務端要判斷請求的來源,及限制跨域(只允許信任的域名訪問),然后除了這些還有一些方法來防止 CSRF 攻擊,下面會通過幾個簡單的例子來詳細介紹 CSRF 攻擊的表現及如何防御。


下面會通過一個例子來講解 CSRF 攻擊的表現是什么樣子的。

實現的例子:

在前后端同域的情況下,前后端的域名都為 http://127.0.0.1:3200, 第三方網站的域名為 http://127.0.0.1:3100,釣魚網站頁面為 http://127.0.0.1:3100/bad.html。


平時自己寫例子中會用到下面這兩個工具,非常方便好用:

http-server: 是基于node.js的HTTP 服務器,它最大的好處就是:可以使用任意一個目錄成為服務器的目錄,完全拋開后端的沉重工程,直接運行想要的js代碼;

nodemon: nodemon是一種工具,通過在檢測到目錄中的文件更改時自動重新啟動節點應用程序來幫助開發基于node.js的應用程序

前端頁面: client.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>CSRF-demo</title>

   <style>

       .wrap {

           height: 500px;

           width: 300px;

           border: 1px solid #ccc;

           padding: 20px;

           margin-bottom: 20px;

       }

       input {

           width: 300px;

       }

       .payInfo {

           display: none;

       }

       .money {

           font-size: 16px;

       }

   </style>

</head>


<body>

   <div class="wrap">

       <div class="loginInfo">

           <h3>登陸</h3>

           <input type="text" placeholder="用戶名" class="userName">

           <br>

           <input type="password" placeholder="密碼" class="password">

           <br>

           <br>

           <button class="btn">登陸</button>

       </div>

       

       

       <div class="payInfo">

           <h3>轉賬信息</h3>

           <p >當前賬戶余額為 <span class="money">0</span>元</p>

           <!-- <input type="text" placeholder="收款方" class="account"> -->

           <button class="pay">支付10元</button>

           <br>

           <br>

           <a target="_blank">

               聽說點擊這個鏈接的人都賺大錢了,你還不來看一下么

           </a>

       </div>

   </div>

</body>

<script>

   const btn = document.querySelector('.btn');

   const loginInfo = document.querySelector('.loginInfo');

   const payInfo = document.querySelector('.payInfo');

   const money = document.querySelector('.money');

   let currentName = '';

   // 第一次進入判斷是否已經登陸

   Fetch('http://127.0.0.1:3200/isLogin', 'POST', {})

   .then((res) => {

       if(res.data) {

           payInfo.style.display = "block"

           loginInfo.style.display = 'none';

           Fetch('http://127.0.0.1:3200/pay', 'POST', {userName: currentName, money: 0})

           .then((res) => {

               money.innerHTML = res.data.money;

           })

       } else {

           payInfo.style.display = "none"

           loginInfo.style.display = 'block';

       }

       

   })

   // 點擊登陸

   btn.onclick = function () {

       var userName = document.querySelector('.userName').value;

       currentName = userName;

       var password = document.querySelector('.password').value;

       Fetch('http://127.0.0.1:3200/login', 'POST', {userName, password})

       .then((res) => {

           payInfo.style.display = "block";

           loginInfo.style.display = 'none';

           money.innerHTML = res.data.money;

       })

   }

   // 點擊支付10元

   const pay = document.querySelector('.pay');

   pay.onclick = function () {

       Fetch('http://127.0.0.1:3200/pay', 'POST', {userName: currentName, money: 10})

       .then((res) => {

           console.log(res);

           money.innerHTML = res.data.money;

       })

   }

   // 封裝的請求方法

   function Fetch(url, method = 'POST', data) {

       return new Promise((resolve, reject) => {

           let options = {};

           if (method !== 'GET') {

               options = {

                   headers: {

                       'Content-Type': 'application/json',

                   },

                   body: JSON.stringify(data),

               }

           }

           fetch(url, {

               mode: 'cors', // no-cors, cors, *same-origin

               method,

               ...options,

               credentials: 'include',

           }).then((res) => {

               return res.json();

           }).then(res => {

               resolve(res);

           }).catch(err => {

               reject(err);

           });

       })

   }

   

</script>


</html>

實現一個簡單的支付功能:


會首先判斷有沒有登錄,如果已經登陸過,就直接展示轉賬信息,未登錄,展示登陸信息

登陸完成之后,會展示轉賬信息,點擊支付,可以實現金額的扣減

后端服務: server.js


const Koa = require("koa");

const app = new Koa();

const route = require('koa-route');

const bodyParser = require('koa-bodyparser');

const cors = require('@koa/cors');

const KoaStatic = require('koa-static');


let currentUserName = '';


// 使用  koa-static  使得前后端都在同一個服務下

app.use(KoaStatic(__dirname));


app.use(bodyParser()); // 處理post請求的參數


// 初始金額為 1000

let money = 1000;


// 調用登陸的接口

const login = ctx => {

   const req = ctx.request.body;

   const userName = req.userName;

   currentUserName = userName;

   // 簡單設置一個cookie

   ctx.cookies.set(

       'name',

       userName,

       {

         domain: '127.0.0.1', // 寫cookie所在的域名

         path: '/',       // 寫cookie所在的路徑

         maxAge: 10 * 60 * 1000, // cookie有效時長

         expires: new Date('2021-02-15'),  // cookie失效時間

         overwrite: false,  // 是否允許重寫

         SameSite: 'None',

       }

     )

   ctx.response.body = {

       data: {

           money,

       },

       msg: '登陸成功'

   };

}

// 調用支付的接口

const pay = ctx => {

   if(ctx.method === 'GET') {

       money = money - Number(ctx.request.query.money);

   } else {

       money = money - Number(ctx.request.body.money);

   }

   ctx.set('Access-Control-Allow-Credentials', 'true');

   // 根據有沒有 cookie 來簡單判斷是否登錄

   if(ctx.cookies.get('name')){

       ctx.response.body = {

           data: {

               money: money,

           },

           msg: '支付成功'

       };

   }else{

       ctx.body = '未登錄';

   }

}


// 判斷是否登陸

const isLogin = ctx => {

   ctx.set('Access-Control-Allow-Credentials', 'true');


   if(ctx.cookies.get('name')){

       ctx.response.body = {

           data: true,

           msg: '登陸成功'

       };


   }else{

       ctx.response.body = {

           data: false,

           msg: '未登錄'

       };

   }

}

// 處理 options 請求

app.use((ctx, next)=> {

   const headers = ctx.request.headers;

   if(ctx.method === 'OPTIONS') {

       ctx.set('Access-Control-Allow-Origin', headers.origin);

       ctx.set('Access-Control-Allow-Headers', 'Content-Type');

       ctx.set('Access-Control-Allow-Credentials', 'true');

       ctx.status = 204;

   } else {

       next();

   }

})


app.use(cors());

app.use(route.post('/login', login));

app.use(route.post('/pay', pay));

app.use(route.get('/pay', pay));

app.use(route.post('/isLogin', isLogin));


app.listen(3200, () => {

   console.log('啟動成功');

});

執行 nodemon server.js,訪問頁面 http://127.0.0.1:3200/client.html


CSRF-demo


登陸完成之后,可以看到Cookie是種到 http://127.0.0.1:3200 這個域下面的。


第三方頁面 bad.html


<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <meta name="viewport" content="width=device-width, initial-scale=1.0">

   <title>第三方網站</title>

</head>

<body>

   <div>

       哈哈,小樣兒,哪有賺大錢的方法,還是踏實努力工作吧!

       <!-- form 表單的提交會伴隨著跳轉到action中指定 的url 鏈接,為了阻止這一行為,可以通過設置一個隱藏的iframe 頁面,并將form 的target 屬性指向這個iframe,當前頁面iframe則不會刷新頁面 -->

       <form action="http://127.0.0.1:3200/pay" method="POST" class="form" target="targetIfr" style="display: none">

           <input type="text" name="userName" value="xiaoming">

           <input type="text" name="money" value="100">

       </form>

       <iframe name="targetIfr" style="display:none"></iframe>

   </div>

</body>

<script>

   document.querySelector('.form').submit();

</script>

</html>

使用 HTTP-server 起一個 本地端口為 3100的服務,就可以通過 http://127.0.0.1:3100/bad.html 這個鏈接來訪問,CSRF攻擊需要做的就是在正常的頁面上誘導用戶點擊鏈接進入這個頁面

CSRF-DEMO


點擊誘導鏈接,跳轉到第三方的頁面,第三方頁面自動發了一個扣款的請求,所以在回到正常頁面的時候,刷新,發現錢變少了。

我們可以看到在第三方頁面調用 http://127.0.0.1:3200/pay 這個接口的時候,Cookie自動加在了請求頭上,這就是為什么 http://127.0.0.1:3100/bad.html 這個頁面拿不到 Cookie,但是卻能正常請求 http://127.0.0.1:3200/pay 這個接口的原因。


CSRF攻擊大致可以分為三種情況,自動發起Get請求, 自動發起POST請求,引導用戶點擊鏈接。下面會分別對上面例子進行簡單的改造來說明這三種情況


自動發起Get請求

在上面的 bad.html中,我們把代碼改成下面這樣


<!DOCTYPE html>

<html>

 <body>

   <img src="http://127.0.0.1:3200/payMoney?money=1000">

 </body>

</html>

當用戶訪問含有這個img的頁面后,瀏覽器會自動向自動發起 img 的資源請求,如果服務器沒有對該請求做判斷的話,那么會認為這是一個正常的鏈接。


自動發起POST請求

上面例子中演示的就是這種情況。


<body>

   <div>

       哈哈,小樣兒,哪有賺大錢的方法,還是踏實努力工作吧!

       <!-- form 表單的提交會伴隨著跳轉到action中指定 的url 鏈接,為了阻止這一行為,可以通過設置一個隱藏的iframe 頁面,并將form 的target 屬性指向這個iframe,當前頁面iframe則不會刷新頁面 -->

       <form action="http://127.0.0.1:3200/pay" method="POST" class="form" target="targetIfr">

           <input type="text" name="userName" value="xiaoming">

           <input type="text" name="money" value="100">

       </form>

       <iframe name="targetIfr" style="display:none"></iframe>

   </div>

</body>

<script>

   document.querySelector('.form').submit();

</script>

上面這段代碼中構建了一個隱藏的表單,表單的內容就是自動發起支付的接口請求。當用戶打開該頁面時,這個表單會被自動執行提交。當表單被提交之后,服務器就會執行轉賬操作。因此使用構建自動提交表單這種方式,就可以自動實現跨站點 POST 數據提交。


引導用戶點擊鏈接

誘惑用戶點擊鏈接跳轉到黑客自己的網站,示例代碼如圖所示


<a >聽說點擊這個鏈接的人都賺大錢了,你還不來看一下么</a>

用戶點擊這個地址就會跳到黑客的網站,黑客的網站可能會自動發送一些請求,比如上面提到的自動發起Get或Post請求。


如何防御CSRF

利用cookie的SameSite

SameSite有3個值: Strict, Lax和None


Strict。瀏覽器會完全禁止第三方cookie。比如a.com的頁面中訪問 b.com 的資源,那么a.com中的cookie不會被發送到 b.com服務器,只有從b.com的站點去請求b.com的資源,才會帶上這些Cookie

Lax。相對寬松一些,在跨站點的情況下,從第三方站點鏈接打開和從第三方站點提交 Get方式的表單這兩種方式都會攜帶Cookie。但如果在第三方站點中使用POST方法或者通過 img、Iframe等標簽加載的URL,這些場景都不會攜帶Cookie。

None。任何情況下都會發送 Cookie數據

我們可以根據實際情況將一些關鍵的Cookie設置 Stirct或者 Lax模式,這樣在跨站點請求的時候,這些關鍵的Cookie就不會被發送到服務器,從而使得CSRF攻擊失敗。


驗證請求的來源點

由于CSRF攻擊大多來自第三方站點,可以在服務器端驗證請求來源的站點,禁止第三方站點的請求。

可以通過HTTP請求頭中的 Referer和Origin屬性。


HTTP請求頭


但是這種 Referer和Origin屬性是可以被偽造的,碰上黑客高手,這種判斷就是不安全的了。


CSRF Token

最開始瀏覽器向服務器發起請求時,服務器生成一個CSRF Token。CSRF Token其實就是服務器生成的字符串,然后將該字符串種植到返回的頁面中(可以通過Cookie)

瀏覽器之后再發起請求的時候,需要帶上頁面中的 CSRF Token(在request中要帶上之前獲取到的Token,比如 x-csrf-token:xxxx), 然后服務器會驗證該Token是否合法。第三方網站發出去的請求是無法獲取到 CSRF Token的值的。

其他知識點補充

1. 第三方cookie

Cookie是種在服務端的域名下的,比如客戶端域名是 a.com,服務端的域名是 b.com, Cookie是種在 b.com域名下的,在 Chrome的 Application下是看到的是 a.com下面的Cookie,是沒有的,之后,在a.com下發送b.com的接口請求會自動帶上Cookie(因為Cookie是種在b.com下的)


2. 簡單請求和復雜請求

復雜請求需要處理option請求。


之前寫過一篇特別詳細的文章 CORS原理及@koa/cors源碼解析,有空可以看一下。


3. Fetch的 credentials 參數

如果沒有配置credential 這個參數,fetch是不會發送Cookie的


credential的參數如下


include:不論是不是跨域的請求,總是發送請求資源域在本地的Cookies、HTTP Basic anthentication等驗證信息

same-origin:只有當URL與響應腳本同源才發送 cookies、 HTTP Basic authentication 等驗證信息

omit: 從不發送cookies.

平常寫一些簡單的例子,從很多細節問題上也能補充自己的一些知識盲點。

日歷

鏈接

個人資料

藍藍設計的小編 http://m.skdbbs.com

存檔

92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线
欧美在线国产精品| 国模一区二区三区白浆| 在线看日韩欧美| 毛片在线导航| 免费短视频成人日韩| 136国产福利精品导航网址| 亚洲欧洲免费无码| 国产极品一区| 亚洲国产视频二区| 欧美日韩中文字幕在线视频| 国产呦精品一区二区三区网站| 中文字幕+乱码+中文字幕一区| 国产精品视频专区| 精品视频国产| 国产成人免费9x9x人网站视频| 色综合天天爱| 国产精品亚洲专一区二区三区| 久久大逼视频| 综合久久久久| 91在线国产观看| 欧美日韩夜夜| 91免费视频网站| 色婷婷av一区二区三区gif| 97久久久精品综合88久久| 欧美人妖巨大在线| 亚洲欧美日本在线| 日韩欧美在线观看强乱免费| 伊人久久大香线蕉综合影院首页| 欧美精品18videos性欧| 秋霞影院一区二区三区| 台湾色综合娱乐中文网| 在线观看亚洲视频| 91久久精品www人人做人人爽| 性感美女一区二区在线观看| 色在线视频观看| 99成人精品| 亚洲精品第1页| 成人精品影视| 日韩欧美一级特黄在线播放| 一个人www视频在线免费观看| 中国人与牲禽动交精品| 亚洲free性xxxx护士白浆| 亚洲欧洲中文日韩久久av乱码| 亚洲高清不卡在线| 在线电影一区二区| 七七成人影院| 1区2区3区国产精品| 国产日韩欧美夫妻视频在线观看| 精品在线免费观看| 国产在线精品一区二区不卡了| 国产亚洲精品久久久久久牛牛| av毛片久久久久**hd| 亚洲国产精品成人av| 国产成人免费av在线| 人人爱人人干婷婷丁香亚洲| 全球av集中精品导航福利| 97精品国产综合久久久动漫日韩| 亚洲欧美日韩区| 999久久精品| 亚洲成人精品在线| 亚洲日本中文字幕免费在线不卡| 欧美大片一区| 亚洲精品a级片| 日本电影一区二区| 国内精品模特av私拍在线观看| 国产一区二区三区四区二区| 欧美一级三级| 亚洲乱码一区二区三区三上悠亚| 日韩免费在线| 麻豆传媒在线免费| 亚洲主播在线观看| 操你啦在线视频| 97超视频免费观看| 色偷偷久久人人79超碰人人澡| 激情综合久久| 亚洲欧美一区二区三区| 午夜欧美理论片| www欧美xxxx| 亚洲国产精品精华液网站| 亚洲午夜三级在线| 欧美日韩精品在线视频| 成人黄在线观看| 国产91在线|亚洲| 欧美另类高清zo欧美| 国产精久久一区二区| 3d精品h动漫啪啪一区二区| 精品999久久久| 久久av老司机精品网站导航| 999国内精品视频在线| 久久精品欧美日韩| 黄色免费网站在线观看| 亚洲精品中文在线观看| 亚洲精品乱码日韩| 国产福利不卡视频| 国产精品一区二区三区不卡| 亚洲电影在线观看| 国产成人看片| 国产成人亚洲欧美| 69久久夜色| 麻豆网站在线观看| 91超碰在线| 欧美人伦禁忌dvd放荡欲情| 开心久久婷婷综合中文字幕| 在线视频成人| 日韩精品在线观看一区二区| 成人黄色av网站在线| 亚洲免费人成在线视频观看| 热三久草你在线| 国产精品亚发布| 极品尤物一区二区三区| 97久久精品人人做人人爽50路| 久久精品国产亚洲7777| 精品高清美女精品国产区| 欧美日韩精品一区二区天天拍小说| 亚洲欧美日韩在线高清直播| 国产精品啊啊啊| 日本午夜一区二区| 韩国av一区二区三区四区| 亚洲成人免费网站| 亚洲精品国产精品粉嫩| 国产一区二区三区日韩精品| 在线日本高清免费不卡| 久久婷婷综合激情| 国产美女性感在线观看懂色av| 小嫩嫩12欧美| 国产精品美女久久久久高潮| 欧美日韩高清一区二区| 亚洲美女激情视频| 亚洲成a人片在线观看中文| 亚洲综合精品一区二区| 亚洲国产精品成人一区二区| 91福利精品第一导航| 日本一区二区三区播放| 国内揄拍国内精品少妇国语| jvid福利写真一区二区三区| 欧美精品在线免费播放| 色婷婷国产精品久久包臀| 国产福利91精品一区二区三区| 日韩专区中文字幕一区二区| 成人午夜电影免费在线观看| 日韩精品小视频| 欧美成年人视频| 欧美精品国产白浆久久久久| 第一sis亚洲原创| 成人午夜在线免费| 91精品蜜臀在线一区尤物| 91在线国产电影| 亚洲一区二区三区中文字幕| 久久国产一区二区| 国产麻豆一区二区三区精品视频| 国产激情久久| 黄在线观看免费网站ktv| 国外成人福利视频| 欧美精品日韩少妇| 精品国产自在久精品国产| 国产精品嫩草影院一区二区| 激情综合网天天干| 欧美三级电影在线播放| 91啪九色porn原创视频在线观看| 91亚洲精品视频在线观看| 黄色欧美在线| 韩国av一区二区三区四区| www免费在线观看| 精品久久中文字幕久久av|