CookieとSessionでログインを理解する
これまで作った試料管理APIサーバーには1つ大きな問題があります。誰でも試料を登録し、修正し、削除できるということです。実験室で機器室の入退室にカードキーが必要なように、Webサービスにも「この人が誰か確認する手続き」— **認証(Authentication)**が必要です。
ログインはどう動くのでしょうか?この質問に答えるにはまず、Webの根本的な特性を理解する必要があります。
HTTPは記憶できない
Webの通信プロトコルであるHTTPは**ステートレス(stateless)**です。サーバーはリクエストを処理しレスポンスを送った瞬間、そのユーザーが誰だったかを即座に忘れてしまいます。
例えると — 受付デスクの担当者が5秒ごとに記憶がリセットされる状態です。「試料S001の結果を教えてください」→ 結果を伝える →(リセット)→「さっき試料S001について聞いたんですが」→「どちら様ですか?」
リクエストのたびに「私は田中研究員です」と再度名乗らなければなりません。この不便さを解消するために1994年にCookieが発明されました。
Cookie:ブラウザに貼る名札
Cookieはサーバーがブラウザに渡す小さなテキストデータです。ブラウザはこのデータを保存しておき、同じサーバーへのリクエスト時に自動で一緒に送ります。
[1] ログインリクエスト
ブラウザ → サーバー: "ID tanaka、パスワード 1234"
サーバー → ブラウザ: "確認完了!このCookieを持ち歩いて"
Set-Cookie: user=tanaka
[2] 以降のリクエスト(自動)
ブラウザ → サーバー: "試料リストをください" + Cookie: user=tanaka
サーバー: "あ、田中研究員だね。試料リストを送るよ"
[3] さらに別のリクエスト(自動)
ブラウザ → サーバー: "S001の詳細" + Cookie: user=tanaka
サーバー: "田中研究員のリクエストだね。S001情報を送るよ"実験室の入退室カードと同じです。一度発行されれば毎回身分を証明する必要がなく、カードを見せるだけで済みます。
ExpressでCookieを扱う
const express = require("express");
const app = express();
app.get("/login", function(req, res) {
res.setHeader("Set-Cookie", "user=tanaka; Path=/");
res.send("ログイン完了");
});
app.get("/whoami", function(req, res) {
const cookies = req.headers.cookie;
res.send("現在のCookie: " + cookies);
});
app.listen(3000);/loginにアクセスするとサーバーがSet-CookieヘッダーでCookieを送り、その後/whoamiにアクセスするとブラウザが自動でCookieを送るのが確認できます。
Cookieの限界:なぜSessionが必要なのか
Cookieだけで認証すると深刻な問題があります。Cookieはブラウザに保存されます。ブラウザの開発者ツールを開けば誰でもCookieの内容を見て、変更することもできます。
もしCookie: user=tanakaのようにユーザー名を直接Cookieに入れると、誰かがCookie: user=adminに書き換えて管理者になりすますことができます。
実験室の例え — 入退室カードに「名前:田中研究」と書いてあって、誰でも修正テープで名前を変えられるなら入退室管理は無意味です。
**Session(セッション)**がこの問題を解決します。
Session:サーバーが管理する入退室記録
Sessionの核心アイデア — 機密情報はサーバーに保存し、ブラウザには識別番号だけを渡す。
[1] ログイン成功
サーバー内部: { session_abc123: { user: "tanaka", role: "researcher" } }
サーバー → ブラウザ: Set-Cookie: session_id=abc123
[2] 以降のリクエスト
ブラウザ → サーバー: Cookie: session_id=abc123
サーバー: abc123セッションを検索 → { user: "tanaka", role: "researcher" }
→ "田中研究員だね"ブラウザが持っているのはabc123という意味のない番号だけです。この番号を変えてみても、サーバーに登録されたセッションでなければ無効です。
実験室の例え — 入退室カードにバーコードだけが印刷されていて、実際の身元情報は警備室のコンピュータにあるのと同じです。カードをコピーしてもバーコードがデータベースになければドアは開きません。
| Cookieのみ | Session(Cookie + サーバー側ストレージ) | |
|---|---|---|
| データの場所 | ブラウザ | サーバー |
| セキュリティ | ユーザーが変更可能(危険) | サーバーのみ変更可能(安全) |
| 容量 | 4KB制限 | サーバーメモリ/DBベース(制限なし) |
| 例え | 名前が書いてある入退室カード | バーコードだけのカード + 警備室DB |
Cookieのセキュリティオプション
Cookie自体にもセキュリティ設定ができます:
Set-Cookie: session_id=abc123; HttpOnly; Secure; Path=/; Max-Age=3600| オプション | 意味 | なぜ必要か |
|---|---|---|
HttpOnly | JavaScriptからCookieにアクセス不可 | 悪意あるスクリプト(XSS)によるCookie窃取を防止 |
Secure | HTTPSでのみCookieを送信 | ネットワーク盗聴によるCookie漏洩を防止 |
Max-Age=3600 | 3600秒(1時間)後に自動削除 | 古いログイン状態が無期限に維持されるのを防止 |
Path=/ | このパス以下のリクエストでのみCookieを送信 | 不要なCookie送信範囲を制限 |
Max-AgeがないCookieはブラウザを閉じると消えます — これを**セッションCookie(Session Cookie)と言います。Max-AgeがあるCookieはブラウザを閉じても残ります — これを永続Cookie(Permanent Cookie)**と言います。
現代の認証:トークンベース
現代のWebサービスではセッションIDの代わりに**JWT(JSON Web Token)**というトークンを使うことが多いです。BioPlaygroundが使うSupabaseもJWTベースの認証です。
原理はセッションと似ていますが、違いがあります:
| セッション方式 | トークン(JWT)方式 | |
|---|---|---|
| サーバー状態 | セッションストレージが必要 | サーバーに保存不要 |
| 検証 | セッションDB照会 | トークン自体の署名検証 |
| スケーラビリティ | サーバーが複数台ならセッション共有が必要 | サーバー間の共有不要 |
深い実装はこの段階では扱いません。重要なのはメカニズムを理解することです:
- ユーザーがID/パスワードを送る
- サーバーが確認し、識別子(セッションIDまたはトークン)を発行
- ブラウザが以降のリクエストに識別子を自動添付
- サーバーが識別子で「この人が誰か」を確認
すべてのログインシステムはこの4ステップの変形です。
やってみよう(Faded Example)
空欄を埋めてCookieベース認証のフローを完成させてください。
// 1. ログイン:サーバーがCookieを発行app.post("/login", function(req, res) {res.setHeader("", "session_id=xyz789; HttpOnly; Path=/");res.json({ message: "ログイン成功" });});// 2. 保護されたAPI:Cookieでユーザー確認app.get("/my-samples", function(req, res) {const cookies = req.headers.;if (!cookies || !cookies.includes("session_id")) {return res.status().json({ error: "ログインが必要です" });}res.json({ samples: ["S001", "S002"] });});
よくあるエラーと解決法
Q: Cookieが送信されません(req.headers.cookieがundefined)
ブラウザの開発者ツール(F12)→ Application → Cookiesで、該当ドメインのCookieが存在するか確認してください。Secureオプションが有効な場合、HTTP(localhost)では送信されません。開発中はSecureを外すかHTTPSを使ってください。
Q: Set-Cookieヘッダーを送ったのにブラウザがCookieを保存しません
フロントエンドとバックエンドのドメイン(またはポート)が異なるとCORSポリシーによりCookieがブロックされます。fetch呼び出し時にcredentials: 'include'オプションを追加し、サーバーでAccess-Control-Allow-Credentials: trueヘッダーを設定してください。
Q: SessionとCookieの違いを一言で説明してください
Cookieは「ブラウザに保存されるデータ」、Sessionは「サーバーに保存されたデータをCookieのIDで見つけるシステム」です。SessionはCookieの上に成り立つ概念です。