Python | Web フレームワーク:Cookie

Python
スポンサーリンク

概要(Cookie=「ブラウザ側にそっと置くメモ」)

Cookie は一言でいうと、

「サーバーがブラウザにそっと渡して、ブラウザ側に保存してもらう小さなメモ」

です。

ブラウザは、そのメモ(Cookie)を同じサイトにアクセスするときに毎回自動で送ってくれます。
サーバー側は、そのメモを見て

どのユーザーか(ログインセッション)
前回の設定(ダークモードかどうか、言語設定など)

などを判別できます。

ここでは、FastAPI を例にしながら、

Cookie がそもそもどう動くのか
Cookie の読み書きの具体的なコード
セッション的な使い方・注意点
セキュリティ設定(HttpOnly, Secure など)の意味

を、初心者向けに丁寧にかみ砕いて説明します。


Cookie の基本イメージ(サーバーとブラウザのやり取り)

サーバーが「Set-Cookie」、ブラウザが「Cookie」で返す流れ

Cookie のやり取りは、ざっくりこういう流れです。

サーバー → レスポンスヘッダで
Set-Cookie: name=value; ...

ブラウザ → その Cookie を保存しておく
次に同じサイトにアクセスするとき、
Cookie: name=value; ...
というヘッダを自動で付けて送る

つまり、

一度 Set-Cookie してしまえば、
その後はブラウザが勝手に Cookie を送り続けてくれる

という仕組みです。

ログイン状態の管理や、
ユーザーごとの設定保持などに向いています。

Cookie の中に何を入れるか

Cookie の「中身」は、基本的には単なる文字列です。
だからといって、

ユーザーIDをそのまま入れる
パスワードを入れる

のは、直球すぎて危険です。

実務では多くの場合、

ランダムなセッショントークン
署名付きトークン(JWT など)

を Cookie に入れて、
サーバー側で「このトークンは有効か?」とチェックする形にします。

ここでは、まずシンプルな文字列から始めて、
あとでセキュリティ上の注意も説明します。


FastAPI で Cookie を「セットする」基本(レスポンス側)

set_cookie を使ってレスポンスに Cookie を乗せる

FastAPI では、レスポンスオブジェクトに対して set_cookie を呼び出すことで
Cookie をセットできます。

最小の例から見てみましょう。

from fastapi import FastAPI
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/set-cookie")
def set_cookie():
    response = JSONResponse(content={"message": "cookie set"})
    response.set_cookie(key="user_id", value="123")
    return response
Python

このエンドポイントにブラウザからアクセスすると、
レスポンスヘッダに次のようなものが付きます。

Set-Cookie: user_id=123; Path=/

ブラウザは user_id=123 を保存し、
次回から同じドメインへのリクエスト時に自動で送ってくれます。

ここでのポイントを整理します。

set_cookie の key が Cookie 名
value が Cookie の値(文字列)
その他、オプション(有効期限、Secure, HttpOnly など)も指定できる

最初は key と value だけで動きを理解して、
慣れてきたらオプションを増やすと良いです。

値だけでなく、有効期限なども指定できる

Cookie には、有効期限や属性をつけられます。

例えば、「30分で消える Cookie」にしたい場合。

from datetime import timedelta

@app.get("/login")
def login():
    response = JSONResponse(content={"message": "logged in"})
    response.set_cookie(
        key="session_token",
        value="RANDOM_TOKEN",
        max_age=30 * 60,  # 秒単位(30分)
    )
    return response
Python

主に使うパラメータには、こんなものがあります。

max_age
Cookie の有効期間(秒)。ブラウザはこの時間が過ぎたら Cookie を消す。

expires
特定の日時まで有効、という指定もできる(通常は max_age で十分)。

path
そのパス以下にだけ Cookie を送る。通常は / にしておけば全パスに送られる。

domain
どのドメインに対して Cookie を送るか。サブドメインをまたぐときなどに指定。

secure
True にすると HTTPS 接続のときだけ Cookie を送る(http では送らない)。本番では基本 True 推奨。

httponly
True にすると JavaScript(document.cookie)から読めなくなる。XSS 対策としてセッション用 Cookie などに必須級。

特に securehttponly は安全性に直結するので、
ログイン系の Cookie を扱うときは強く意識してください。


FastAPI で Cookie を「読む」基本(リクエスト側)

Request オブジェクトから Cookie を取り出す

セットした Cookie は、次回以降のリクエストで自動で送られてきます。
FastAPI では、Request 経由で簡単に読めます。

from fastapi import FastAPI, Request

app = FastAPI()

@app.get("/whoami")
def who_am_i(request: Request):
    user_id = request.cookies.get("user_id")
    return {"user_id": user_id}
Python

request.cookies は dict のようなオブジェクトで、
Cookie 名(key)と値(value)にアクセスできます。

Cookie がない場合は None になるので、

ログインしていない → user_id が None
ログイン済み → user_id が何か入っている

というような判定ができます。

Depends を使って「Cookie チェックを共通化」する

エンドポイントごとに request.cookies を読むのではなく、
Dependency(依存関係)として切り出しておくとスッキリします。

from fastapi import Depends, HTTPException, status

def get_current_user_id(request: Request) -> str:
    user_id = request.cookies.get("user_id")
    if not user_id:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Not logged in",
        )
    return user_id

@app.get("/my-page")
def my_page(user_id: str = Depends(get_current_user_id)):
    return {"message": f"Hello user {user_id}"}
Python

このようにしておくと、

「user_id Cookie がないと 401」
「あるなら user_id をエンドポイントに渡す」

という共通ロジックを get_current_user_id に押し込めます。

ログイン必須の全ての API で、
user_id: str = Depends(get_current_user_id) と書くだけで済みます。


Cookie を使った「超シンプルなログイン風フロー」の例

1. ログインエンドポイントで Cookie をセットする

とても単純な「なんちゃってログイン」を考えてみます。
実際のアプリではパスワードチェックなどが入りますが、
ここでは流れを掴むために「ユーザー名を受け取るだけ」にします。

from fastapi import FastAPI, Response

app = FastAPI()

@app.post("/login")
def login(username: str, response: Response):
    # 本当はここでユーザー名+パスワードをチェックする
    response.set_cookie(key="username", value=username, httponly=True)
    return {"message": f"logged in as {username}"}
Python

ポイントは Response を引数に受け取っていることです。
FastAPI は、引数に Response 型を入れると、
レスポンスオブジェクトを注入してくれます。

そこに対して set_cookie を呼び出して、Cookie を設定します。

2. ログイン後のページで Cookie を読む

次に、「ログインしている前提で動くエンドポイント」を作ります。

from fastapi import Request, HTTPException, status

@app.get("/dashboard")
def dashboard(request: Request):
    username = request.cookies.get("username")
    if not username:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Not logged in",
        )
    return {"message": f"Welcome, {username}"}
Python

ブラウザ側の流れとしては、

POST /login?username=taro などを叩く
レスポンスで Set-Cookie: username=taro を受け取る
ブラウザが Cookie を保存
その後 /dashboard にアクセスするとき、
ブラウザが Cookie: username=taro を自動で付けて送る

サーバー側では、
その Cookie を見て「taro としてログインしている」と判断します。

これが Cookie を使った認証の超基本イメージです。

実務では、username を直接 Cookie に入れるより、

ランダムなセッショントークン
署名付きのセッションID

などを入れて、
サーバー側で user_id と紐付ける形にします。


Cookie を使うときのセキュリティ上の注意(ここは少し深掘り)

1. センシティブな情報を生のまま入れない

Cookie は、基本的にクライアント側に丸見えです。
開発者ツールから簡単に中身が見えてしまいます。

そのため、

パスワード
生の個人情報(住所、電話番号など)

を直接入れるのはNGです。

ユーザーIDくらいならまだしも、「セッション用のランダムなトークン」を入れて、
サーバー側の DB で実体情報と紐付ける、という設計のほうが安全です。

2. HttpOnly を付けて JavaScript から読めなくする

XSS(クロスサイトスクリプティング)攻撃で、
サイト内に悪意ある JavaScript が紛れ込んだ場合、

document.cookie で Cookie が盗まれるリスクがあります。

ログインセッション用 Cookie などは、
必ず httponly=True を付けておくのが望ましいです。

response.set_cookie(
    key="session_token",
    value="RANDOM",
    httponly=True,
)
Python

HttpOnly を付けると、
JavaScript からは Cookie が読めなくなります(ブラウザがブロックする)。
サーバーとの通信には自動で付くので、認証には支障ありません。

3. Secure を付けて HTTPS のときだけ送る

secure=True を付けると、
HTTP では Cookie を送らず、HTTPS 接続時だけ送るようになります。

平文 HTTP のままだと、
ネットワーク上で Cookie が盗み見される可能性があります。

本番でログインセッションを扱うなら、

HTTPS(TLS)必須
セッション Cookie は secure=True

はほぼセットで考えるべきです。


Cookie と Bearer トークン(ヘッダ)との違いのイメージ

Cookie:ブラウザに任せて自動送信される「小さなメモ」

Cookie は、主にブラウザが使う仕組みです。

一度 Set-Cookie を受け取れば、
ブラウザが自動で Cookie を付けてくれるので、
フロント側で毎回トークンをヘッダにセットする必要がありません。

Web アプリ(ブラウザ+サーバー)の世界では、
セッション管理に非常によく使われます。

Bearer トークン:クライアントが自分でヘッダに付ける「入館証」

一方、Bearer トークンは、
クライアントが自分で Authorization: Bearer ... をセットします。

ブラウザ・モバイル・他のサーバーなど、
いろいろなクライアントで使いやすく、API 用途に向いています。

どっちが絶対にいい、という話ではなく、

ブラウザだけを相手にする Web アプリ → Cookie セッションが自然
外部サービスからも叩かれる API → Bearer トークン(ヘッダ)が自然

という使い分けが多いです。

FastAPI はどちらにも対応しやすいので、
アプリの性質に合わせて選ぶ感じです。


まとめ(Cookie は「ブラウザと仲良くするための持ち物」)

Python Web フレームワーク(FastAPI)での Cookie を整理すると、こうなります。

  • Cookie は、サーバーが Set-Cookie でブラウザに渡し、ブラウザが次回以降のリクエストで自動的に Cookie ヘッダとして送り返してくれる「小さなメモ」。
  • FastAPI ではレスポンスの set_cookie で Cookie をセットし、Request の request.cookies から読むことができる。ログインセッションやユーザー設定などの継続的な情報に向いている。
  • Dependency(get_current_user など)として Cookie チェックを切り出すと、「ログイン必須」のエンドポイントに共通の認証ロジックを簡単に適用できる。
  • セッション系の Cookie には、HttpOnly や Secure を付けることが重要で、生のパスワードやセンシティブ情報を直接 Cookie に入れないようにする。
  • Cookie はブラウザ主体、Bearer トークンはクライアント主体(ヘッダ)という違いがあり、Web アプリ寄りなら Cookie、API 利用が多いなら Bearer という選び分けがよく使われる。

タイトルとURLをコピーしました