この記事を読んでできること
CSRFトークンの実装ができる
やりたいこと
ログイン認証機能がないパブリックサイトでクライアントへのCSRFトークンの付与と認証をstarlette-csrfというライブラリで実装する
記述しないこと
- FastAPIの実装について
- Cookieについて
使用技術
バックエンド
- Python 3.10.4
- Fast API 0.78.0
- starlette-csrf 1.4.4
※ トークンを発行して認証するシステムは完全にstarlette-csrfというライブラリに任せます
発行と認証の流れ(フロントとの疎通)
- GETメソッドでトークンを発行するAPIをフロントエンドに叩いてもらう
- クライアントのCookieファイルに
csrftoken
というkeyでランダム文字列のトークンが格納される - リクエスト毎にクライアントから
x-csrftoken
というkeyでヘッダーを送ってもらう - ライブラリで最初に発行したものと一致するか確認し、問題がなければリクエストを通す
実装例
main.py
import os
from fastapi import FastAPI
from starlette_csrf import CSRFMiddleware
# 環境変数を含めたクラス
from config import settings
from routers import router
app = FastAPI()
# CSRFミドルウェアの設定を格納する辞書を定義します
cookie_attribute = {
"secret": settings.SECRET_KEY,
"cookie_samesite": None,
"cookie_secure": True,
}
# デバッグモードではない場合、クッキーのドメインを設定します
# クッキードメインを設定します。これにより、特定のドメインにクッキーが適用されます
if not settings.DEBUG_FLAG:
cookie_attribute["cookie_domain"] = settings.COOKIE_DOMAIN
# CSRFミドルウェアを追加し、cookie_attribute辞書を引数として渡します
app.add_middleware(CSRFMiddleware, **cookie_attribute)
app.include_router(router.router)
routers/router.py
@router.get("/api/get_csrf_token", tags=["token"])
def get_csrf_token():
return "トークンを発行しました。"
starlette-csrfについての詳細
starlette-csrfの仕様
- 安全なHTTPメソッド(GET、HEAD、OPTIONS、TRACE)でリクエストされた場合、set-cookie ヘッダーを使用してクライアントのクッキーに csrftoken というキーでトークンを格納する
- クライアントがPOSTリクエストを送信する際、x-csrftoken というキーでトークンを含むヘッダーを送信することが期待される
- サーバー側で、クッキーに保存されたトークンとヘッダーに含まれたトークンの値を比較し、検証する
- トークンが一致する場合、リクエストは正常に処理される
- トークンが一致しない場合は、403 Forbiddenエラーが返される
Cookie属性
READMEに詳細記載あり
- cookieの名前
- パス
- Secure属性
- Samesite属性等を変更することができる
処理の検証結果
- 現状、単純なapi/get_csrf_tokenというエンドポイントを作成し、tokenをセットする
- POSTMANで
- ヘッダにx-csrftokenを用意しない場合は、403エラー
- ヘッダにx-csrftokenを用意したが、valueが一致しない場合も403エラー
- ヘッダにx-csrftokenを用意し、valueが一致した場合、リクエストを処理する