04 Python Flask 教學10 所有文章

【Flask教學系列】實作 Flask CSRF Protection

Flask教學_CSRF_WTF_Max行銷誌

ㄧ. 什麼是 CSRF?

CSRF 是一種 Web 上的攻擊手法,全名是 Cross Site Request Forgery,跨站請求偽造,又稱 one-click attack。

CSRF 經典案例是 2007年在 Gmail 信件的 CSRF 攻擊:Warning: Google Gmail security failure

簡單來說,CSRF 就是在使用者不知情的情況下,讓使用者的瀏覽器自動送出請求給目標網站,利用使用者當前的身份去做一些未經過授權的操作以達攻擊目的。

也因為 Server 不知道收到的 request 到底是從信任的來源發出的還是不信任的來源,所以如果能解決來源信任的問題,就可以避免掉 CSRF 的攻擊。

圖源:CSRF 攻擊是什麼? 簡述

二. 如何避免 CSRF 攻擊?

本篇會敘述兩種 Flask 實作避免 CSRF 攻擊的方法,分別是 CSRF token 和 SameSite cookie:

方法一. CSRF token

為了解決來源信任問題,由 Server 產生 token 存放在 session 中,並在前端頁面中所有表單加上一個 hidden 的欄位放 token,當 Server 接收到使用者發送的請求時,會驗證 session 中的 token 與請求中的 token 是否相同,來判斷請求來源是否是可信。

▍Flask 實作 CSRF token

安裝 flask-wtf

pip install flask-wtf

從 flask_wtf 中載入 CSRFProtect,並設定 SECRET_KEY

import os
from flask_wtf.csrf import CSRFProtect

app.config['SECRET_KEY'] = os.urandom(24)

csrf = CSRFProtect(app)

在所有表單中加入 hidden 欄位 csrf_token

<form method="post">
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
    <input type="text" name="email" class="form-control form-control-lg" placeholder="[email protected]">
</form>

如果是使用 ajax 或 axios 可在 header 中加入 csrf-token

<script>
var csrf = "{{ csrf_token() }}";
axios.post('/auth/login', data, {
        headers: {'x-csrf-token': csrf}
        })
</script>

那這為什麼可以防禦 CSRF 攻擊呢?這個方法,之所以有用是因為 form 表單的 token 雖然可以隨便修改,但攻擊者並不知道正確的 csrf_token 值是什麼,也沒辦法猜到,兩者不可能匹配, request 就不被信任,所以自然就無法進行攻擊了。

關於前後端分離 SPA 架構下的模式如何使用 CSRF Token,推薦以下兩篇介紹的 client side 的 Double Submit cookie:

方法二. SameSite cookie

Same-site cookies (née “First-Party-Only” (née “First-Party”)) allow servers to mitigate the risk of CSRF and information leakage attacks by asserting that a particular cookie should only be sent with requests initiated from the same registrable domain.
‘SameSite’ cookie attribute – Chrome Platform Status

SameSite 的用途為防止瀏覽器因跨站請求傳送 cookies,目的是降低跨站資料外洩與 CSRF 攻擊的風險。SameSite 設有三個數值層級,分別為 Strict, Lax 和 None,由嚴格至寬鬆不同程度地限制 cookies 的傳輸。

  1. 當 cookie 中設定了 SameSite = ‘Strict’ 的話,代表著這個 cookie 只允許 same site 情況下使用。
  2. 而 SameSite = ‘Lax’ 放寬部份限制,只限制當使用 POST、PUT 或 DELETE 方法進行跨站請求時,就不會帶上此 cookie。

但目前並非所有的瀏覽器版本都支持 SameSite 的功能,所以並非加上了 SameSite=’Strict’ 就可以完全避免 CSRF 的攻擊。

關於查詢各瀏覽器版本的 SameSite 支援可以從這邊查詢:Can I use… Support tables for HTML5, CSS3, etc

▍Flask 實作 SameSite cookie

@app.route("/set")
def setcookie():
    resp = make_response('Setting cookie!')
    resp.set_cookie(key='framework', value='flask', expires=time.time()+6*60, secure=False, httponly=False, samesite=None)
    return resp

可以看到我們在 set cookie 時,有多設定三個參數:secure=False, httponly=False, samesite=None

  1. secure:如果設定為 True,表示此 cookie 僅在 Https 時才被傳送
  2. httponly:如設定為 True,表示 JavaScript 的Document.cookie API 無法取得 HttpOnly cookies
  3. samesite:可以設定成 Strict、Lax 和 None,功能如同剛剛所述

關於更多 cookie 的操作可以參考此篇:
Flask教學 5分鐘快速設定 Flask取得cookie資料 | Max行銷誌

關於更多 Chrome cookie SameSite 的更新可以參考此篇:
不想失去追蹤受眾資料 ?! 你該知道的Chrome cookie更新 – TenMax ad Tech Lab

最後~

▍回顧本篇 Flask CSRF Protection,我們討論了以下內容:

  1. 什麼是 CSRF?
  2. 如何避免 CSRF 攻擊?
    • 方法一. CSRF token
    • 方法二. SameSite cookie

關於 Flask 教學的延伸閱讀:

▍關於 Flask 教學系列目錄:

▍其他 Flask 相關教學:

那麼有關於 [Flask教學系列] 實作 Flask CSRF Protection 的介紹就到這邊告一個段落囉!有任何問題可以在以下留言~

有關 Flask 的最新文章,都會發佈在 Max 的 Facebook 粉絲專頁,如果想看最新更新,還請您按讚或是追蹤唷!

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *