かゆい所に手が届く解説

ざっくり解説!クッキーとセッションの違いとそれぞれの役割について

「ログインしたのに、ページを移動したら情報が消えてしまった…」「Cookieとセッションって何が違うの?」——Flaskを使い始めたばかりのころ、こんな疑問を持ったことはありませんか?

WebアプリではHTTPというプロトコルを使って通信しますが、HTTPはステートレス(状態を持たない)という性質があります。つまり、リクエストのたびに「誰が送ってきたのか」をサーバーは忘れてしまいます。ログイン状態やカートの中身を覚えておくために必要なのが、CookieとSession(セッション)の仕組みです。

この記事では、Cookie・クライアントサイドセッション・サーバーサイドセッションの3つをわかりやすいたとえ話とFlaskの実装コードを交えて解説します。「なんとなく動かせているけど、違いがよくわからない」という方はぜひ最後まで読んでみてください!

🍪 Cookieとは何か?

web security cookie browser
web security cookie browser / Photo by Abdullah Bin Mubarak via Pexels

仕組みと特徴

Cookieとは、サーバーがブラウザに保存させる小さなデータのことです。サーバーがHTTPレスポンスヘッダーに Set-Cookie を含めてブラウザに送ると、ブラウザはそのデータを保存し、次回以降のリクエスト時に自動的にサーバーへ送り返します。

たとえ話でいうと、「鍵付きロッカーに入れた荷物」のイメージです。サーバーが荷物(データ)を用意してロッカーに入れ、あなた(ブラウザ)はその荷物を読んだり、自分で中身を書き換えたりすることもできます。

  • データはブラウザ(クライアント側)に保存される
  • 文字列として中身が見える・書き換えられる(暗号化しない場合)
  • 有効期限(expires)を設定できる
  • ドメイン・パスを指定してスコープを絞れる

具体的なユースケース

  • 「次回もログイン状態を保持する」チェックボックス
  • ECサイトの言語設定・表示設定の保存
  • アクセス解析用のトラッキングID
  • セッションIDの受け渡し(後述)

FlaskでCookieを操作する

from flask import Flask, request, make_response

app = Flask(__name__)

# Cookieをセットするエンドポイント
@app.route('/set_cookie')
def set_cookie():
    resp = make_response('Cookieをセットしました!')
    # max_age=秒数で有効期限を設定
    resp.set_cookie('username', 'taro', max_age=60*60*24)  # 1日間
    return resp

# Cookieを読み取るエンドポイント
@app.route('/get_cookie')
def get_cookie():
    username = request.cookies.get('username', 'ゲスト')
    return f'こんにちは、{username}さん!'

# Cookieを削除するエンドポイント
@app.route('/delete_cookie')
def delete_cookie():
    resp = make_response('Cookieを削除しました!')
    resp.delete_cookie('username')
    return resp

このように、Flaskでは make_response() で生成したレスポンスオブジェクトに対して set_cookie() を呼ぶことで簡単にCookieを操作できます。

🔐 セッションとは何か?

セッションとは、一連のユーザー操作(ページ遷移など)にまたがって状態を保持する仕組みの総称です。「ログインしてからログアウトするまでの一連の流れ」がセッションのイメージです。

セッションにはCookieを内部的に利用するものも多く、「CookieとSessionは別物」というよりは「SessionはCookieを使って実現することが多い上位の概念」と理解するとスッキリします。

セッションの実装方式は大きく2種類あります。

⚖️ クライアントサイドセッション vs サーバーサイドセッション

① クライアントサイドセッション(Flaskのデフォルト)

たとえ話でいうと、「ガラスケースに入れた荷物」です。中身は(Base64デコードすれば)誰でも見えますが、改ざんするとサーバーに検知されます。

Flaskのデフォルトセッションがこの方式で、セッションデータ全体を署名付きCookie(JWTに近い形式)としてブラウザに保存します。secret_key を使った署名により、データの改ざんは検知できますが、中身はBase64デコードで読めてしまいます

  • ✅ サーバーにデータを保存しなくてよい(スケールしやすい)
  • ✅ シンプルで設定が少ない
  • ❌ Cookieのサイズ制限(約4KB)がある
  • ❌ パスワードなどの機密情報は入れてはいけない
  • ❌ セッションを強制失効させにくい
from flask import Flask, session, redirect, url_for, request

app = Flask(__name__)
app.secret_key = 'your-very-secret-key-here'  # 必須!署名に使う

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        # ※本来はパスワード検証も行うこと
        session['username'] = username  # セッションに保存
        return redirect(url_for('dashboard'))
    return '''
        <form method="post">
          <input name="username" placeholder="ユーザー名">
          <button type="submit">ログイン</button>
        </form>
    '''

@app.route('/dashboard')
def dashboard():
    if 'username' not in session:
        return redirect(url_for('login'))
    return f'ようこそ、{session["username"]}さん!'

@app.route('/logout')
def logout():
    session.clear()  # セッションを削除
    return 'ログアウトしました'

② サーバーサイドセッション

たとえ話でいうと、「サーバーが管理している鍵付きロッカー」です。セッションデータはサーバー(RedisやDBなど)に保存され、ブラウザには「ロッカーの鍵(セッションID)」だけがCookieとして渡されます。鍵を持っていてもロッカーの中身は見えません。

  • ✅ 機密情報をブラウザに渡さない
  • ✅ セッションIDを無効化するだけで即座にログアウトできる
  • ✅ 大きなデータも保存できる
  • ❌ RedisやDBなどのサーバー側ストレージが必要
  • ❌ 設定がやや複雑

Flaskでサーバーサイドセッションを実現するには Flask-Session 拡張が便利です。

# pip install Flask-Session redis
from flask import Flask, session
from flask_session import Session

app = Flask(__name__)

# サーバーサイドセッションの設定(Redisを使う例)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['SESSION_TYPE'] = 'redis'          # 保存先をRedisに指定
app.config['SESSION_PERMANENT'] = False
app.config['SESSION_USE_SIGNER'] = True       # Cookie側も署名する

import redis
app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:6379')

Session(app)  # Flask-Sessionを初期化

@app.route('/set')
def set_session():
    session['user_id'] = 42  # RedisにID=42を保存、Cookieにはセッションキーのみ
    return 'セッションをサーバー側に保存しました'

@app.route('/get')
def get_session():
    user_id = session.get('user_id', 'なし')
    return f'user_id: {user_id}'

🛡️ セキュリティの注意点

1. secret_key は必ず強力なものを使う

secret_key はCookieの署名・検証に使われます。短すぎたり推測しやすい文字列を使うと、攻撃者に偽のセッションを作られてしまいます。以下のように生成しましょう。

import secrets
print(secrets.token_hex(32))  # 64文字のランダムな16進数文字列を生成
# 例: 'a3f2...b9c1' のような文字列が出力される

2. HttpOnly フラグ

HttpOnly を設定すると、JavaScriptからCookieにアクセスできなくなります。XSS(クロスサイトスクリプティング)攻撃でCookieを盗まれるリスクを下げられます。Flaskでは set_cookie()httponly=True を追加します。

3. Secure フラグ

Secure フラグを設定すると、HTTPS通信のときだけCookieが送信されます。本番環境では必ず設定してください。

resp.set_cookie(
    'session_id',
    value='xxxx',
    httponly=True,   # JavaScriptからアクセス不可
    secure=True,     # HTTPSのみ送信
    samesite='Lax'   # CSRF対策にもなる
)

📊 まとめ比較表

項目 Cookie クライアントサイドセッション サーバーサイドセッション
データ保存場所 ブラウザ ブラウザ(署名付き) サーバー(Redis/DB)
中身の可視性 見える Base64で見える 見えない
改ざん検知 ✅(署名で検知)
強制失効 △(有効期限のみ) ❌(困難) ✅(即座に可能)
サーバー負荷 低い 低い やや高い
主なユースケース 設定保存・トラッキング 小規模アプリのログイン 本番アプリのログイン管理

🤔 どれを使うべき?

  • 📝 学習・プロトタイプ段階クライアントサイドセッション(Flaskデフォルト)で十分。設定が少なく手軽に試せます。
  • 🔒 ログイン情報・決済など機密データが絡むサーバーサイドセッションを選びましょう。データがブラウザに漏れません。
  • 🌐 言語設定・テーマなどユーザー好みの保存Cookieが適しています。長期間保持でき、機密性も不要です。

まとめ

今回はCookie・クライアントサイドセッション・サーバーサイドセッションの違いを解説しました。

  • 🍪 Cookie:ブラウザに保存する小さなデータ。見える・書ける「鍵付きロッカー」
  • 📦 クライアントサイドセッション:署名でデータを守るが中身は見える「ガラスケース」
  • 🔑 サーバーサイドセッション:データをサーバーに隠し、鍵(ID)だけブラウザに渡す

最初はFlaskのデフォルトセッションで動かしてみて、本番環境に移行するタイミングでサーバーサイドセッション+セキュリティフラグの設定を意識するのがおすすめです。ぜひ実際にコードを書いて動かしてみてください!

📚 関連商品・おすすめ書籍

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

もしも

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

初心者に定番のPython入門書

Amazonで見る
実践Claude Code入門―現場で活用するためのAIコーディングの思考法

もしも

実践Claude Code入門―現場で活用するためのAIコーディングの思考法

AIコーディングの現場活用法を学ぶ一冊

Amazonで見る
Python Web開発実践入門 ―― FastAPIによるWebAPI開発と非同期処理

もしも

Python Web開発実践入門 ―― FastAPIによるWebAPI開発と非同期処理

FastAPIでWebAPI開発を実践的に学ぶ

Amazonで見る

※本記事にはアフィリエイトリンクが含まれます。

ABOUT ME
やまちゃん
これまで学生と社会人を合わせて5000人以上にプログラミング学習を指導。 ゼロからイチをわかりやすく解説する専門家として活動しており、本業ではArduinoを用いたIoT開発とロボットプログラミングが専門。 Pythonを用いたアプリ開発、ウェブアプリケーションの開発で業務の効率化をサポートしています。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です