6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Set-Cookie] Set-CookieでCookieファイルに値を格納できない問題の原因と解決方法

Last updated at Posted at 2022-12-16

この記事を読んでできること

サーバーサイド側からSet-CookieでクライアントのCookieファイルにトークンを格納できない問題を解決できる

なぜこの記事を書くことにしたか

サーバーサイド側からSet-CookieでクライアントにCookieの値を格納しようとしたが、格納されず、解決にかなりの時間を割くことになったため

やりたいこと

サーバーサイド側からSet-CookieでクライアントのデベロッパーツールのApplicationのCookieファイルにCSRFトークンの値を格納したい

記述しないこと

  • FastAPIの実装について
  • Cookieについて

使用技術

バックエンド

  • 言語
    • Python 3.10.4
  • フレームワーク
    • Fast API 0.78.0
  • データベース
    • MySQL 8.0.31
  • ライブラリ
    • starlette-csrf==1.4.4

※ちなみに

  • トークンを発行するシステムはstarlette-csrfというライブラリに任せます
    • ライブラリの仕様については最後尾に記載します
  • ローカル環境で検証しています

現状

トークンを発行するAPIをフロントエンドに叩いてもらい、NetworkにはレスポンスヘッダにSet-Cookieが渡されていることが確認できる
しかし、Cookieファイルにはトークンが格納されていない

main.py
import os

from fastapi import FastAPI
from starlette_csrf import CSRFMiddleware

from routers import router


app = FastAPI()

app.add_middleware(
    CSRFMiddleware,
    secret=os.environ["SECRET_KEY"],
)

app.include_router(router.router)
routers/router.py
@router.get("/api/get_csrf_token", tags=["token"])
def get_csrf_token():
    return "トークンを発行しました。"

Set-Cookieは返ってきているが、cookieファイルにはトークンがない

スクリーンショット 2022-12-16 10.53.01.png (207.1 kB) スクリーンショット 2022-12-16 10.50.07.png (164.0 kB)

原因

Cookieの属性について把握をしていなかったため
Response HeaderのSet-Cookieの横にある小さなアテンションマークにカーソルを合わせることで表記される

以下がその要因である

  1. このSet-Cookieヘッダーによるクッキーの設定は、「SameSite=None」属性は持っているものの、「SameSite=None」を使用するために必要な「Secure」属性を持っていなかったため、ブロックされる
  2. このSet-Cookieヘッダーによるクッキーの設定の試みは、「Same-Site=Lax」属性を持っているものの、トップレベルナビゲーションに対する応答ではないクロスサイトの応答から来るため、ブロックされる

今回であれば、starlette-csrfライブラリのデフォルトがSame-Site=Laxのため、2番である
SameSite、Secure属性については以下の記事を参照
SameSite cookies

トップレベルナビゲーションとは

そのリクエストによってアドレスバーに記載されているURLが変化するものであるナビゲーションのこと
以下の記事を参照
SameSite 属性を使った Cookie のセキュアな運用を考える

該当箇所を修正

今回はhttp://localhost:3000からhttp://127.0.0.1:8000/api/get_csrf_tokenへリクエストしており、全く別物のオリジンからくるレスポンスのため、Cookieの属性をNone+Secureにする
starlette-csrfライブラリのREADMEを参照し、以下のように引数を渡す
これによって、CookieにSameSite=None, Secure=True属性を付与することができる

main.py
app.add_middleware(
    CSRFMiddleware,
    secret=os.environ["SECRET_KEY"],
    cookie_samesite=None,
    cookie_secure=True,
)

Cookieの属性がSameSite=None、Secureであることが確認できる

スクリーンショット 2022-12-16 10.54.23.png (206.0 kB) 無事、 Cookieが格納されていることを確認できる スクリーンショット 2022-12-16 10.44.48.png (114.7 kB)

starlette-csrfについての詳細

starlette-csrfの仕様

  1. 安全なhttpメソッド({“GET”, “HEAD”, “OPTIONS”, “TRACE”})でリクエストされたとき、set-cookieでクライアントのcookieにcsrftokenというkeyで格納する
  2. クライアントからPOSTリクエストされたとき、x-csrftokenというkeyでcookie headerが送信されることを期待する
  3. cookieとheaderのtokenの値を比較検証する
  4. それらが一致した場合、リクエストは処理される
  5. 一致しない場合は、403 Forbiddenエラー応答が返される

処理の検証結果

  • 現状、単純なapi/get_csrf_tokenというエンドポイントを作成し、tokenをセットする
  • POSTMANで
    • ヘッダにx-csrftokenを用意しない場合は、403エラー
    • ヘッダにx-csrftokenを用意したが、valueが一致しない場合も403エラー
    • ヘッダにx-csrftokenを用意し、valueが一致した場合、リクエストを処理する

参考

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?