認識 CSRF 攻擊

CSRF 為 Cross-Site Request Forgery 縮寫,意指「跨站點偽造請求」

CSRF 也被稱為 one-click attack 或者 session riding,通常縮寫為 CSRF 或者 XSRF,是一種挾制使用者在當前已登錄的 Web 應用程式上執行非本意的操作的攻擊方法。跟跨網站指令碼(XSS)相比,XSS 利用的是使用者對指定網站的信任,CSRF 利用的是網站對使用者網頁瀏覽器的信任 ( 出自維基百科 )。

事情是這樣的

有一天,小明使用瀏覽器登入網站 (例如:http://foo.bar),登入成功之後,在瀏覽器上會把小明受驗證過的資料,記在 cookies,讓小明之後的訪問操作,不用在重新登入驗證。

此時,有個壞人寫個了網站,裡面全是小明有興趣的內容,吸引小明去瀏覽,網站背後的原始碼卻埋藏了「自動訪問 http://foo.bar 的請求,例如請求變更密碼」,這樣小明就不知道自己送了這些請求,而已經通過身份驗證的 cookies 也被一併送去給伺服器,在 cookies 還是有效的情況下,請求會被驗證通過,導致「因為被惡意網站偽造的頁面內容載入,或假按鈕被點擊,不被小明發覺的請求操作被發出去,而且還能請求成功」,這就是所謂的 CSRF。

簡單來說,就是「在非本人預期的情況下,訪問其他(惡意)網頁內容,送出非預期的請求且成功」。

如何防止 CSRF 攻擊

參考文章「 讓我們來談談 CSRF 」這篇講得蠻詳細的,在此僅用自己的話紀錄一下,以在系統架構為前後端分離時:

  1. 在「客端」生成暗號, token (此 token 只有小明才知道,而偽造網站不知道/不能生成),記在 cookies 中
  2. 暗號會在每次請求完之後刷新
  3. 在伺服器端檢查,該請求是否包含合法暗號,以防止偽造請求攻擊
  4. 因為偽造網站「沒辦法在瀏覽器寫入不同網址的 cookies」,因此伺服器可以得知請求中的暗號是不是同源產生的
  5. 若是偽造網站,他只能隨便塞一個自己建的暗號給伺服器 (取不到 cookies),透過 form 或 ajax 送出,when form.暗號 != request.cookies.暗號 表示這個請求是偽造的請求

當然也可以單純把 token 寫在伺服器端 (用 session),讓前端去塞入到每個請求中,這樣也可以驗證暗號是不是對的,也就是説「只要這暗號,是別人沒辦法建立的,只有我能用」的前提之下才有效。

Laravel 提供的 CSRF 作法

Laravel 提供你可輕鬆在原始碼中,加入 csrf token (暗號),以防止 CSRF 攻擊發生,以下是幾種產生 token 的方式:

  • 透過 helper csrf_token() 產生 csrf token
  • 透過 blade 語法 @csrf 產生 csrf token
  • 透過寫一個 api,去跟伺服器要回 csrf token (有點怪,請求數量會變成兩倍)
  • 客端產生隨機數,當成 token,由 cookies 同源機制去判斷是不是「我家的客端」(題外話,若 cookies 有辦法被第三者寫入,或透過 CORS 取用,則無法真正防止 CSRF 攻擊發生)

最後

這兩天看了很多篇關於 CORS、CSRF 的說明,大致上能理解其描述的案例、場景、以及大概是如何實作的。有任何問題或想法,歡迎留言交流,如果寫的內容有錯誤的地方,希望能不吝指點,感謝。

另外,筆者一併整理了討論到 CSRF 時,可一併吸收研讀的主題

參考連結



文章作者: littlebookboy
永久鏈結: https://blog.genesu.me/2021/06/learn-about-csrf/
許可協議: 署名-非商業性使用-相同方式共享 4.0 國際(CC BY-NC-SA 4.0)