0から分かるCookieとセッション 〜Webの記憶喪失問題と通行手形のハイジャック〜
1. 3行でわかる結論と全体図
- インターネットの通信(HTTP)は本来、**「1回やり取りしたら、直前の相手を完全に忘れる重度の記憶喪失(ステートレス)」**という性質を持っています。
- 画面を移動するたびにパスワードを聞かれないように、サーバーはログイン成功時に**「あなたの整理券番号(セッションID)」**をブラウザに渡し、ブラウザのポケット(Cookie)に保存させます。
- 以後ブラウザは、ページを開くたびに整理券(Cookie)を自動で提示するため、ログイン状態が保たれますが、**この整理券を盗まれるとパスワードなしで一瞬で侵入(セッションハイジャック)**されます。
2. なぜ生まれたか?(昔の不便と解決策)
第5回で学んだとおり、Webの通信(HTTP)は「手紙(リクエスト)を送って、返事(レスポンス)をもらう」というシンプルな往復です。 この仕組みを作った技術者たちは、サーバーに余計な負荷をかけないため、あえて「過去の会話を一切覚えない(状態を持たない=ステートレス)」というルールにしました。
しかし、1994年頃に「Webでネットショッピング(ECサイト)をしよう」と考えたとき、致命的な問題が発生しました。
- 商品Aをカゴに入れる → 「カゴに入れました!」
- 次の画面(商品B)を開く → 「初めまして!あなたは誰ですか?カゴの中身?知りません」
通信のたびに相手を忘れてしまうため、「買い物カゴ」や「ログイン」といった機能がまったく作れなかったのです。
そこで1994年、当時世界シェア1位だったブラウザ「ネットスケープ」の技術者ルー・モントゥリ氏が考案したのが、**「Cookie(クッキー)」**でした。
こうして、Webは「記憶喪失」のままで、実質的に「記憶を保てる」ようになりました。
3. 仕組みの種明かし(3つの重要ルール)
Cookieとセッションの違いを正確に理解するための3つのキーワードです。
| 専門用語 | 小学生でもわかる例え | 実際にやっていること |
|---|---|---|
| Cookie(クッキー) | 客のポケットの「メモ用紙」 | サーバーから「これをメモしておいて」と指示され、ブラウザが手元のパソコンの中に大切に保管する小さなテキストデータです。 |
| セッション (Session) | お店側の「来店客台帳」 | サーバー側にあるデータベースです。「整理券番号 #999 の客は、田中さんで、カゴにはリンゴが入っている」という詳細な状態を記録しています。 |
| セッションハイジャック | 整理券の「すり・横取り」 | 悪いハッカーが他人のCookie(整理券番号)を盗み取り、自分のブラウザにくっつけてアクセスすることで、パスワードを知らなくてもその人になりすます攻撃です。 |
触って試す:ログインの記憶体験
🔑 ログインの記憶体験(疑似モデル)
ボタンを順に押して、サーバーがあなたを「覚える」様子を再現します。①ログインすると整理券(セッションID)が発行され、ブラウザのポケット(Cookie)に保存されます。②別のページを開くとどうなるか、Cookieを消すとどうなるか、試してみてください。
💻 ブラウザのポケット
🏢 サーバーの台帳
まずは「① ログインする」を押してください。
4. 作って暴く!(Google Colab 15分ミニ実験)
「本当にCookieがないと記憶喪失になるのか?」「他人のCookieを貼るとどう乗っ取れるのか?」を、Pythonで実験してみましょう。 以下のリンクからGoogle Colabを開き、コードを実行してみてください。
実験①:記憶喪失サーバーと、Cookieによる「マイページ維持」
サーバーの記憶喪失の様子と、Cookieによってログインが維持される通信の裏側を再現します。
# === 実験①:Cookieを使ったログイン維持と記憶喪失の再現 ===
# サーバー側が管理する「セッション台帳」
server_sessions = {}
# 1. サーバーの処理関数
def handle_request(path, cookie=None):
if path == "/login":
# ログイン成功時:ランダムな整理券(セッションID)を発行して台帳に書く
session_id = "token_abc123_secret"
server_sessions[session_id] = {"user": "加藤洋平", "role": "管理者"}
return f"【HTTP 200 OK】ログイン成功!\n👉 サーバーからの指示: Set-Cookie: session_id={session_id}"
elif path == "/mypage":
# マイページ要求時:送られてきたCookieの整理券を台帳と照合
if cookie and cookie in server_sessions:
user_info = server_sessions[cookie]
return f"【HTTP 200 OK】ようこそ、{user_info['user']}さん!(権限: {user_info['role']})"
else:
return "【HTTP 401 Unauthorized】🚨 誰だか分かりません!ログインし直してください。"
# --- 実験スタート ---
print("1. まずログインします...")
login_res = handle_request("/login")
print(login_res)
print("\n2. 【Cookieを付けずに】マイページを開こうとする(記憶喪失の罠)")
no_cookie_res = handle_request("/mypage", cookie=None)
print(no_cookie_res)
print("\n3. 【もらったCookieを付けて】マイページを開く")
my_cookie = "token_abc123_secret"
with_cookie_res = handle_request("/mypage", cookie=my_cookie)
print(with_cookie_res)
実験①の種明かし
2番目のアクセスでは、1秒前にログインしたばかりなのにサーバーは「誰だか分かりません!」と怒っています。
3番目のように、手紙のヘッダーに session_id=token_abc123_secret というCookieを添えた瞬間だけ、サーバーは台帳を引いて「あ、加藤さんですね」と思い出すことができました。
実験②:セッションハイジャック!悪人が他人の整理券で侵入する
もし悪人が、何らかの方法(カフェのWi-Fi盗聴や第3回の不正スクリプト)でこの整理券 token_abc123_secret を盗み見たらどうなるかを実験します。
# === 実験②:他人のCookieを使ったなりすまし(セッションハイジャック) ===
# ハッカーは加藤さんのIDもパスワードも一切知らない!
# しかし、盗み見た「整理券番号(Cookie)」だけを持っている
stolen_cookie = "token_abc123_secret"
print("😈 ハッカーが盗んだCookieを自分のブラウザに貼り付けてアクセスします...")
hacker_res = handle_request("/mypage", cookie=stolen_cookie)
print("\n👉 サーバーの反応:")
print(hacker_res)
if "ようこそ" in hacker_res:
print("\n💥 【乗っ取り成功】パスワードを1文字も知らなくても、加藤さんとしてログインできました!")
実験②の種明かし
サーバーは「送られてきた整理券番号が台帳にあるかどうか」しか見ていません。 そのため、整理券を提示してきたのが本人なのか、盗んだハッカーなのかを区別することができないのです。
5. 現実世界での悪用と限界(次回への伏線)
このセッションハイジャックを防ぐため、現代のエンジニアはCookieに**3つの厳重な鍵(属性)**をかけています。
Secure属性:暗号化された安全な通信(HTTPS)のときしかCookieを送らない(Wi-Fi盗聴の防止)。HttpOnly属性:ブラウザ内のJavaScriptプログラムからCookieを読み取れなくする(第3回で学んだスクリプト攻撃の防止)。SameSite属性:他の偽サイトから勝手にCookieを使わせない(勝手な送金注文などの防止)。
🌐 ここまでの技術がつながった瞬間
第5回から第7回までで、私たちはWebの背骨をすべて解体しました。
- 第5回(DNS):看板(URL)から数字の住所(IPアドレス)を突き止める。
- 第6回(暗号・認証):公開鍵暗号を使って、パスワードを送らずに本人証明する。
- 第7回(Cookie):記憶喪失の通信に「通行手形」を持たせ、ログイン状態を保つ。
日常で何気なく開いているWebサイトは、この精巧な仕組みの積み重ねで動いています。 しかし、ここで最後の巨大な謎が残ります。
「世界中には、いまこの瞬間も何百億ページものWebサイトが存在している。Googleはどうやって、あなたが検索窓に打ち込んだキーワードの答えを、0.01秒で探し出しているのか?」
まさか、何百億ページを1ページずつ最初から読んでいるわけがありません。 次回・第8回は、現代ITの頂点に君臨する**「検索エンジンの心臓部(転置インデックスとクローラー)」**を自作し、第2シーズンのグランドフィナーレを迎えます。