Table
ㄧ. 什麼是 CSRF?
CSRF 是一種 Web 上的攻擊手法,全名是 Cross Site Request Forgery,跨站請求偽造,又稱 one-click attack。
CSRF 經典案例是 2007年在 Gmail 信件的 CSRF 攻擊:Warning: Google Gmail security failure
簡單來說,CSRF 就是在使用者不知情的情況下,讓使用者的瀏覽器自動送出請求給目標網站,利用使用者當前的身份去做一些未經過授權的操作以達攻擊目的。
也因為 Server 不知道收到的 request 到底是從信任的來源發出的還是不信任的來源,所以如果能解決來源信任的問題,就可以避免掉 CSRF 的攻擊。
二. 如何避免 CSRF 攻擊?
本篇會敘述兩種 Flask 實作避免 CSRF 攻擊的方法,分別是 CSRF token 和 SameSite cookie:
方法一. CSRF token
為了解決來源信任問題,由 Server 產生 token 存放在 session 中,並在前端頁面中所有表單加上一個 hidden 的欄位放 token,當 Server 接收到使用者發送的請求時,會驗證 session 中的 token 與請求中的 token 是否相同,來判斷請求來源是否是可信。
▍Flask 實作 CSRF token
安裝 flask-wtf
1 |
pip install flask-wtf |
從 flask_wtf 中載入 CSRFProtect,並設定 SECRET_KEY
1 2 3 4 5 6 |
import os from flask_wtf.csrf import CSRFProtect app.config['SECRET_KEY'] = os.urandom(24) csrf = CSRFProtect(app) |
在所有表單中加入 hidden 欄位 csrf_token
1 2 3 4 |
<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
1 2 3 4 5 6 |
<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 的傳輸。
- 當 cookie 中設定了 SameSite = ‘Strict’ 的話,代表著這個 cookie 只允許 same site 情況下使用。
- 而 SameSite = ‘Lax’ 放寬部份限制,只限制當使用 POST、PUT 或 DELETE 方法進行跨站請求時,就不會帶上此 cookie。
但目前並非所有的瀏覽器版本都支持 SameSite 的功能,所以並非加上了 SameSite=’Strict’ 就可以完全避免 CSRF 的攻擊。
關於查詢各瀏覽器版本的 SameSite 支援可以從這邊查詢:Can I use… Support tables for HTML5, CSS3, etc
▍Flask 實作 SameSite cookie
1 2 3 4 5 |
@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
- secure:如果設定為 True,表示此 cookie 僅在 Https 時才被傳送
- httponly:如設定為 True,表示 JavaScript 的Document.cookie API 無法取得 HttpOnly cookies
- samesite:可以設定成 Strict、Lax 和 None,功能如同剛剛所述
關於更多 cookie 的操作可以參考此篇:
Flask教學 5分鐘快速設定 Flask取得cookie資料 | Max行銷誌
關於更多 Chrome cookie SameSite 的更新可以參考此篇:
不想失去追蹤受眾資料 ?! 你該知道的Chrome cookie更新 – TenMax ad Tech Lab
最後~
▍回顧本篇 Flask CSRF Protection,我們討論了以下內容:
- 什麼是 CSRF?
- 如何避免 CSRF 攻擊?
- 方法一. CSRF token
- 方法二. SameSite cookie
關於 Flask 教學的延伸閱讀:
▍關於 Flask 教學系列目錄:
▍其他 Flask 相關教學:
- 【Flask教學系列】Flask 為甚麼需要 WSGI 與 Nginx
- 【Flask教學系列】Flask-SQLAlchemy 資料庫連線&設定入門 (一)
- 【Flask教學系列】Flask-JWT-Extended 實作
- 【Flask教學系列】實作 Flask CORS
- 【Flask教學系列】實作 Flask CSRF Protection
- 【Flask教學系列】實作 Dockerfile + nginx + ssl + Flask 教學 (附GitHub完整程式)
那麼有關於 [Flask教學系列] 實作 Flask CSRF Protection 的介紹就到這邊告一個段落囉!有任何問題可以在以下留言~
有關 Flask 的最新文章,都會發佈在 Max 的 Facebook 粉絲專頁,如果想看最新更新,還請您按讚或是追蹤唷!