談談SPA的CSRF問題

本文示例基于Vue.js + Egg.js 代碼參考csrf

目錄

示例

首先 我們通過如下示例來看一下SPA(單頁面應用)的CSRF攻擊

服務

cnpm i -g egg-init
egg-init --type=simple saas-server

cd saas-server && cnpm i

cnpm run dev
vim app/router.js
'use strict';

let count = 0;

module.exports = app => {
  const { router } = app;
  router.post('/login', async ctx => {
    ctx.session.user = 'user';
    ctx.body = { message: 'login success' };
    ctx.status = 200;
  });
  router.post('/count', async (ctx, next) => {
    if (!ctx.session.user) {
      ctx.body = { message: 'need login' };
      ctx.status = 403;
      return;
    }
    await next();
  }, async ctx => {
    ctx.body = { message: 'count ' + count++ };
    ctx.status = 200;
  });
};

vim config/config.default.js
'use strict';

module.exports = appInfo => {
  const config = exports = {};

  config.keys = appInfo.name + '_1533543342201_7043';

  config.middleware = [];

  config.security = {
    csrf: {
      enable: false,
    },
  };

  return config;
};

  • 測試
curl -X POST localhost:7001/count # {"message":"need login"}

curl -c cookies -X POST localhost:7001/login # {"message":"login success"}

curl -b cookies -X POST localhost:7001/count # {"message":"count 0"}

跨域

cnpm i --save egg-cors
vim config/plugin.js
'use strict';

exports.cors = {
  enable: true,
  package: 'egg-cors',
};

vim config/config.default.js
'use strict';

module.exports = appInfo => {
  const config = exports = {};

  config.keys = appInfo.name + '_1533543342201_7043';

  config.middleware = [];

  config.security = {
    csrf: {
      enable: false,
    },
  };

  config.cors = {
    credentials: true,
    origin: 'http://localhost:8080',
    allowMethods: 'HEAD,OPTIONS,GET,PUT,POST,DELETE,PATCH',
  };

  return config;
};

前端

cnpm i -g @vue/cli
vue create saas-client

cd saas-client

yarn serve
vim src/App.vue
<template>
    <div id="app">
        <div>
            <button v-on:click="login">登錄</button>
            <a>{{message1}}</a>
        </div>
        <div>
            <button v-on:click="count">計數</button>
            <a>{{message2}}</a>
        </div>
    </div>
</template>

<script>
export default {
    name: 'app',
    data() {
        return {
            message1: '',
            message2: '',
        }
    },
    methods: {
        login() {
            fetch('http://localhost:7001/login', { method: 'POST', credentials: 'include' })
                .then(res => {
                    return res.json();
                }).then(json => {
                    this.message1 = json;
                }).catch(error => {
                    this.message1 = error;
                });
        },
        count() {
            fetch('http://localhost:7001/count', { method: 'POST', credentials: 'include' })
                .then(res => {
                    return res.json();
                }).then(json => {
                    this.message2 = json;
                }).catch(error => {
                    this.message2 = error;
                });
        },
    }
}
</script>

<style>
</style>

  • 測試

使用瀏覽器打開http://localhost:8080

點擊"登錄"和"計數"按鈕后 效果如下

spa-csrf-01.png

攻擊

vue create csrf-client

cd csrf-client

yarn serve
vim src/App.vue
<template>
    <div id="app">
        <form action="http://localhost:7001/count" method="POST">
            <input type="submit" value="點擊中大獎">
        </form>
    </div>
</template>

<script>
export default {
    name: 'app',
}
</script>

<style>
</style>

  • 測試

使用瀏覽器打開http://localhost:8081

點擊"點擊中大獎"按鈕后 效果如下

spa-csrf-02.png
spa-csrf-03.png

使用瀏覽器打開http://localhost:8080

點擊"計數"按鈕后 效果如下

spa-csrf-04.png

小結

通過上述示例 我們知道想要成功進行CSRF攻擊有如下兩個條件

  • 條件1: 繞過瀏覽器跨域限制 例如: 上述<form>標簽 詳見Laravel框架 之 CSRF

  • 條件2: 基于cookie存儲的session鑒權 AJAX請求會自動帶上cookie導致鑒權通過

對于前后端未分離的項目

  • 條件1 無法回避

  • 條件2 可以在<form>或<meta>添加隱藏的csrf-token來保證請求的有效性 詳見CSRF 保護

而對于前后端分離的項目

  • 條件1: 同樣無法回避

  • 條件2: 可以通過使用除cookie外的其他瀏覽器存儲 例如: sessionStorage或localStorage

因此 對于前后端分離的SPA應用 推薦使用基于非cookie存儲的token鑒權 詳見JWT入門Laravel框架 之 Passport

問題

將token存儲于sessionStorage或localStorage中 會引起共享問題

例如 cookie的域名為".yourdomain.com" 那么"yourdomain.com"和"app.yourdomain.com"都可以訪問該cookie

但是 存儲于sessionStorage或localStorage中的token卻不能在不同域名(甚至subdomain)中共享

因此

  • 可以將token存儲于域名為".yourdomain.com"的cookie中

并且

  • 此時的cookie只做存儲而非鑒權 即服務端并不依賴request中的cookie進行權限校驗

參考

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容

  • 小確幸/感恩:昨晚陪萌讀書,發現孩子成語接龍前100個成語中大部分會背了,而且好幾個成語能接上兩個成語:如:用舍行...
    書琴001閱讀 161評論 0 0
  • 尊敬的老師們,全天下最愛學習最有能量的伙伴們:大家現在好!我是你們最值得信賴的朋友忠偉姐姐。通過這幾天的學習和交流...
    小忠偉閱讀 409評論 0 5
  • 關于雞蛋哥哥的介紹 他不在意弟弟比他大 因為呆在雞蛋里好玩的事很多 比如 可以躲在各種地方捉迷藏(看到雞蛋哥哥躲在...
    秀琴sukin閱讀 1,968評論 0 2