本記事について
当サイトを閲覧いただきありがとうございます。 本記事はシリーズ『生成AI時代のアーキテクチャ超入門』の「ソフトウェアアーキテクチャ」カテゴリ最終記事(第7弾)として、サーバ側の認証・セッションについて解説する記事です。
本記事の問いは「ログイン後のセッションをどう持つか」サーバセッション vs JWT・OAuth 2.0/OIDC・Cookie属性の安全設定まで扱い、「同一ドメインはCookie、横断はJWT」という実務判断軸を示します。
| 扱う | 扱わない(→別記事) |
|---|---|
| サーバセッション・JWT・OAuth・Cookieサーバ設定 | 認証強度(MFA / Passkey / IDaaS)・認可(IAM)・ブラウザ防御(XSS / CSP) |
このカテゴリの他の記事
認証と認可の違い
アプリにログイン機能を実装するとき、しばしば混同されるのが認証(Authentication)と認可(Authorization)です。この2つは目的が全く異なるため、設計段階で明確に分けて考える必要があります。
認証は「あなたは誰ですか」を確認する処理で、パスワードやPasskeyによって本人確認を行います。認可は「あなたは何ができますか」を決める処理で、ログイン済みの利用者に対して「管理画面を表示してよいか」「このデータを編集してよいか」といった権限を判定します。
| 用語 | 内容 | 英語(略称) |
|---|---|---|
| 認証 | 誰なのかを確認する | Authentication(AuthN) |
| 認可 | 何ができるかを決める | Authorization(AuthZ) |
「ログインできたから管理者操作もできる」は典型的な混同。認証と認可は別物として設計します。
セッション管理の2大方式
認証後、ユーザーが再度ログインせずにAPIを使い続けられるようにする仕組みをセッション管理と呼びます。方式は大きく「サーバ側に状態を保持する方式」と「クライアント側に持たせる方式」の2つに分かれます。
| 方式 | 状態の保持場所 | 特徴 |
|---|---|---|
| サーバセッション(Cookie) | サーバ側(Redis等) | 即時失効可能・実績豊富 |
| JWT(トークン) | クライアント側 | ステートレス・スケール容易 |
どちらが優れているということはなく、「同一ドメインの普通のWebアプリならサーバセッション」、マイクロサービス横断や外部連携を含むAPIならJWT、というのが使い分けの定石です。
サーバセッション方式
サーバセッションは、ログイン成功時にサーバーがランダムなセッションIDを発行し、クライアントのCookieに保存させる方式です。以降のリクエストはこのCookieを携えてサーバーに届き、サーバーはセッションIDから Redis 等のストアを参照してユーザー情報を取り出します。
古くから使われている方式で、即時失効が可能(Redisから消せばセッション無効)、情報漏洩時の被害を局所化しやすい、ライブラリが成熟しているという強みがあります。一方、セッションストアを別途運用する必要があり、サーバが横に並ぶ構成(スケールアウト)では全台でストアを共有する必要があります。
| 強み | 弱み |
|---|---|
| 即時失効が可能(ログアウト処理が簡単) | セッションストアが必要 |
| 実績豊富・ライブラリ成熟 | ステートフル(サーバ側に状態あり) |
| 漏洩時の被害を局所化しやすい | スケール時に共有が必要 |
同一ドメインの一般的なWebアプリ・SPAなら、サーバセッションで十分かつ安全です。
JWT(JSON Web Token)方式
JWT は、ユーザー情報そのものを署名付きのトークンに詰めてクライアントに渡す方式です。サーバーは毎回トークンの署名を検証するだけで認証できるため、サーバー側に状態を持たない(ステートレス)のが最大の特徴です。
マイクロサービスが複数立ち並ぶ構成では、各サービスが独立して認証処理できる強みがあります。一方で即時失効が困難(トークンは期限まで有効)、秘密鍵が漏れると全ユーザー偽装可能、ペイロードは誰でも Base64 でデコードできるといった注意点があります。
Header.Payload.Signature
───── ──────── ─────────
アルゴ ユーザー 改竄検知の
リズム 情報 ための署名
JWTの罠
JWTを採用する場合、以下の落とし穴を理解しておく必要があります。特に「失効の困難さ」はJWTの根本的な性質で、回避策には別途仕組みが必要です。
- 失効できない:JWTは期限まで有効なので、漏洩しても即座に無効化できない。対策:短命のAccess Token + httpOnly CookieにいれたRefresh Tokenの組み合わせが定石
- 秘密鍵漏洩は致命的:署名秘密が漏れると、全ユーザーになりすましたトークンを作られる
- ローカルストレージに保存するとXSSに弱い:XSS(Cross-Site Scripting、悪意あるスクリプトを埋め込む攻撃)で簡単に盗まれる。httpOnly Cookieに保存するのが安全
- ペイロードに機密情報を入れない:Base64エンコードは暗号化ではありません。誰でもデコードできる
「Access Token(短命)+ Refresh Token(長命・httpOnly)」が現代のJWT運用の定石です。
OAuth 2.0 / OIDC
OAuth 2.0 は「認可の委譲」を行うためのプロトコルです。「このアプリに Google Drive の読み取り権限を渡す」のように、第三者アプリにAPIアクセス権限を渡す仕組みとして広く使われています。
OpenID Connect(OIDC)はOAuthを土台にして認証を標準化した拡張で、「Googleでログイン」などの機能はOIDCで実現されています。
これらは標準化されたプロトコルなので、自前で実装せず Auth0 / Firebase Auth / Cognito / Okta などの認証サービスに委譲するのが現代の主流です。自前実装は脆弱性のリスクが高く、多要素認証やPasskey対応まで含めると運用負荷が大きくなります。
| プロトコル | 役割 |
|---|---|
| OAuth 2.0 | 第三者アプリにAPIアクセス権限を委譲する |
| OpenID Connect(OIDC) | OAuthを土台にした認証用の拡張プロトコル |
代表例:Google / Apple / GitHub ログイン / 社内SSO(Okta, Auth0)
認証は外部サービスに委譲が標準。自前実装は避けるのが安全です。
OAuthの主要フロー
OAuthには用途別にいくつかのフローがあります。Web・モバイルアプリでは「Authorization Code + PKCE」が事実上の標準で、それ以外のフローを新規採用する場面はほとんどありません。
判断軸は「誰が何を守るか」です。Authorization Codeフローは認可コードをブラウザ経由で受け取り、サーバー側でトークンと交換する2段構えの仕組みで、トークンそのものがブラウザの履歴やURLに残らないため漏洩リスクが低く抑えられます。
これにPKCEを組み合わせることで、途中で認可コードを横取りされても攻撃者がトークンに交換できない仕組みになり、SPAやモバイルのような「秘密を保持できないクライアント」でも安全に使えます。
sequenceDiagram
participant U as ユーザー
participant C as クライアント<br/>(SPA/モバイル)
participant A as 認可サーバ
participant R as リソースサーバ<br/>(API)
U->>C: ログイン操作
C->>C: code_verifier 生成<br/>code_challenge = SHA256
C->>A: 認可リクエスト<br/>+ code_challenge
A->>U: ログイン画面
U->>A: 認証情報入力
A->>C: 認可コード(短命)
C->>A: トークン交換<br/>code + code_verifier
A->>A: code_verifier の<br/>SHA256 が code_challenge と一致するか検証
A->>C: Access Token + Refresh Token
C->>R: API呼び出し<br/>+ Access Token
R->>C: APIレスポンス
| フロー | 用途 |
|---|---|
| Authorization Code + PKCE(Proof Key for Code Exchange、認可コード横取り対策) | SPA・モバイル・Webアプリ(標準) |
| Client Credentials | サーバ間通信(ユーザーが介在しない) |
| Device Code | TV・CLI など入力困難な端末 |
| 非推奨(セキュリティ上の問題あり) | |
| 非推奨(パスワードを第三者に渡す設計) |
新規実装ではPKCE付きAuthorization Code一択。古い情報源のImplicit / Password Grantは使いません。
ケース別の選び方
従来型Webアプリ(同一ドメイン)
サーバセッション(Cookie)。最も枯れた方式で、ログアウト処理も簡単。特別な理由がなければこれを既定に置きます。
SPA + API(同一ドメイン)
サーバセッション(Cookie)。「SPAだからJWT」は誤解。httpOnly Cookieで十分安全です。
マイクロサービス・横断認証
JWT + Refresh Token。ステートレスな性質がサービス間の独立性を高めます。
外部ログイン連携(Google・GitHub等)
OpenID Connect(OIDC)。Auth0 や Firebase Auth に委譲するのが現実的。
企業の社内システム(SSO必要)
OIDC + SSO基盤(Okta / Auth0 / Entra ID)。人事異動での権限変更も一元管理できます。
Cookieの設定
サーバセッションを使う場合も、JWTをCookieに入れる場合も、Cookie属性の設定が安全性を大きく左右します。これらはセキュリティインシデントの半数以上を防ぐ基礎対策です。
| 属性 | 役割 |
|---|---|
| HttpOnly | JavaScriptから読めないようにする(XSS対策) |
| Secure | HTTPS通信でのみ送信する |
| SameSite=Lax または Strict | CSRF(Cross-Site Request Forgery、利用者のログイン状態を悪用した偽リクエスト攻撃)を防ぐ |
| Domain / Path | 送信範囲を最小限に絞る |
| Max-Age / Expires | 有効期限を明示する |
「HttpOnly + Secure + SameSite=Lax」はWebアプリの最低ラインです。これを欠くのは論外です。
実装イメージとしては Express / Hono / Next.js いずれも下記のような形が定番です。
// 推奨: Cookie で受け渡し(XSS から守られる)
res.cookie("session", token, {
httpOnly: true, // JS から読めない(XSS 対策)
secure: true, // HTTPS のみ送信
sameSite: "lax", // CSRF 対策
maxAge: 60 * 60 * 1000, // 1 時間
path: "/",
});
// 非推奨: localStorage 保存(XSS 一発で漏洩)
localStorage.setItem("token", token);
JWT を採用する場合も、保管場所は localStorage ではなくHttpOnly Cookieにし、Refresh Token はさらに別 Cookie かサーバ側 KV ストアに分離します。「JWT=localStorage」という組み合わせは、2024年以降のセキュリティガイドラインでは事実上の禁則です。
セッション・トークン有効期限の数値Gate
※ 2026年4月時点の業界相場値です。テクノロジー・人材市場の変化で陳腐化するため、定期的にアップデートが必要です。
セッション設計は「なんとなく」で決めると必ず脆弱性を生むため、具体的な数値基準を最初に決めます。下記が業界の定番値です。
| 設定項目 | 推奨値 | 理由 |
|---|---|---|
| Access Token(JWT)有効期限 | 15分 | 漏洩時の被害最小化 |
| Refresh Token(httpOnly Cookie)有効期限 | 14〜30日 | ユーザー利便性との両立 |
| セッションCookie 有効期限 | 30分〜24時間(業務次第) | 業務形態による |
| Session IDエントロピー | 128bit以上 | 推測不能な長さ |
| JWT署名アルゴリズム | RS256 / EdDSA | HS256 は対称鍵、鍵配布に注意 |
JWT none アルゴリズム | 絶対に禁止 | 2018年ライブラリ脆弱性で多発 |
| Refresh Token Rotation | 有効化 | 盗難検知で全セッション失効 |
| CSRFトークン | SameSite=Lax + CSRF token | 二重防御 |
JWTのアルゴリズムは RS256(RSA署名)または EdDSA を使い、noneアルゴリズムは絶対に許可しない。2018年に複数のJWTライブラリで none を許可する脆弱性が多発した過去があります。
Refresh Tokenは使用ごとに新しい値を発行(Rotation)し、古い値が再利用されたらセッション全失効という運用が現代の定石です。
セッション・トークン運用の鬼門
セッション実装で事故る典型パターンを整理します。どれもアカウント乗っ取り・成りすましに直結します。
| 禁じ手 | なぜダメか |
|---|---|
| JWTをlocalStorageに保存 | XSSで簡単に盗まれる。httpOnly Cookieが鉄則 |
| Cookieに HttpOnly / Secure / SameSite を設定しない | XSS・盗聴・CSRFの全てが成立する |
| 「SPAだからJWT」の誤解でCookieセッションを避ける | 同一ドメインならCookieセッションの方が安全かつシンプル |
| JWTの失効設計なし | 漏洩時に無効化できない。短命Access + httpOnly Refreshが定石 |
JWTで none アルゴリズムを許可 | 署名検証なしで偽造可能。2018年にライブラリ脆弱性が多発 |
| OAuth Implicit Flow / Password Grant を新規採用 | 2020年以降は非推奨。Authorization Code + PKCEが標準 |
| Refresh Token Rotationなし | 盗難を検知できない。使用ごとに再発行が現代の定石 |
| Session IDのエントロピーが弱い | 推測で乗っ取り。128bit以上の乱数が必須 |
| Cookie属性をデフォルト任せ | 未設定だとSecureもHttpOnlyも効かない。明示設定必須 |
| ログアウト時にCookie削除のみ | サーバ側Refresh Tokenが生き残る。失効API必須 |
認証方式そのもの(パスワード保存・MFA・Passkey)の鬼門は別カテゴリ「セキュリティアーキテクチャ」を参照してください。本記事はセッション技術の鬼門に限定しています。
AI時代の視点
AI駆動開発(バイブコーディング)が前提になると、セッション実装は「AIが正しく書ける領域」と「書かせてはいけない領域」がきっぱり分かれます。
OAuth / OIDC / JWT のような標準化されたプロトコルに従う部分はAIが正確に書けますが、Cookie属性の明示・Refresh Token Rotation・失効APIのような「運用上の細かい設定」は落としがちです。
| AI時代に有利 | AI時代に不利 |
|---|---|
| OAuth / OIDC 標準フローの実装 | 独自のトークン設計 |
| 短命Access Token + httpOnly Refresh Token | 長命・localStorage保存 |
| Cookie属性をコードで明示(HttpOnly等) | デフォルト任せ・暗黙の設定 |
| SDK呼び出し(Auth.js・Clerk SDK等) | 自前のセッション管理 |
セッション技術でAIに任せるなら、「標準ライブラリやSDKの呼び出しに寄せる」のが安全です。Cookie属性・有効期限・Rotationの有無といった「数値や設定値」は、AIが書いたあとに人間が数値Gate表と突き合わせて確認するルーチンを組みます。
AI時代のセッションは「標準プロトコル + 明示設定」デフォルト任せが最大の事故源です。
よくある勘違い
- 「SPAだからJWT」 → 同一ドメインなら Cookie セッションで十分です。JWTは運用上の難しさが増える。この誤解は根深い
- 「JWTはlocalStorageに保存するもの」 → XSSで盗まれる典型パターン。httpOnly Cookieに入れるのが正解
- 「JWTは失効できない前提で運用する」 → 短命Access Token + Refresh Token Rotation を組めば事実上の失効が可能です。設計の問題であってJWTの宿命ではない
- 「Cookie属性はデフォルトで安全」 → HttpOnly も Secure も SameSite もデフォルトでは付きません。明示設定が必須
「JWTなら新しくてイケてる」の錯覚(業界事例)
2010年代後半のSPAブームで広まった誤解に、「SPAなんだからJWTをlocalStorageに入れるのが現代的」という刷り込みがあります。同一ドメインで動く普通のWebアプリでも、なぜかCookieセッションを避けてJWTに飛び付く事例が後を絶ちませんでした。
2017年頃に同じ思い込みでJWT + localStorage 構成を組み、後輩にXSS対策のレビューを受けて「これlocalStorageに置いていいんですか」と指摘されてようやく気付いた、という体験談はよく聞きます。
結果として、XSS一発で全ユーザーのJWTが流出し、しかもJWTの性質上即時失効できないため、攻撃者は有効期限まで好き放題という事例が繰り返し報告されています。Slackが2022年にGitHub経由で従業員トークンを盗まれた件も、仕組み自体はごく一般的なトークン認証で、コードは正しく動いていた構成でした。それでも保管場所と失効運用の甘さが突かれる、という典型例です。
JWTはマイクロサービス横断・外部連携のように「ステートレスでないと成立しない場面」で光る技術で、同一ドメインの普通のWebアプリには過剰装備です。サーバセッション(Cookie)で済むなら、そちらの方が安全でシンプル。この判断を後回しにすると、あとで書き換える羽目になります。
「新しい方が正しい」は認証では通用しません。枯れた方式が選ばれ続けるのは理由があるのです。
決めるべきこと — あなたのプロジェクトでの答えは?
以下の項目について、あなたのプロジェクトの答えを1〜2文で言語化してみてください。曖昧なまま着手すると、必ず後から「なぜそう決めたんだっけ」が問われます。
- セッション方式(サーバセッション / JWT / ハイブリッド)
- Cookie属性(HttpOnly / Secure / SameSite)
- Access Token有効期限 / Refresh Token Rotation
- JWT署名アルゴリズム(RS256 / EdDSA、
none禁止) - OAuthフロー(Authorization Code + PKCE ほぼ一択)
- ログアウト時の処理(失効APIの実装)
- Session IDエントロピー(128bit以上)
認証方式(MFA・Passkey・IDaaS選定・パスワードポリシー)は別カテゴリ「セキュリティアーキテクチャ」の認証設計記事の決定項目です。
最終的な判断の仕方
セッション設計の核心は「ドメイン構造で方式を決める」ことです。同一ドメインのWebアプリはサーバセッション(Cookie)で十分安全かつシンプル、マイクロサービス横断・外部連携を伴うAPIはJWT + Refresh Token Rotation、外部サービス連携はOIDCに寄せる。この判断軸をブラさないことが設計の出発点です。
「SPAだからJWTをlocalStorage」は2010年代の刷り込みで、XSS一撃で全ユーザー漏洩する古典的事故。httpOnly Cookie + サーバセッションか、JWTでもhttpOnly Cookie保管の方が安全です。
もう一つの軸は「標準プロトコルに寄せて、設定値を明示する」こと。OAuth 2.0 / OIDC / PKCEは標準化されており、AIもこれらは正確に書けます。独自トークン設計・独自フローは脆弱性の温床。
Cookie属性(HttpOnly / Secure / SameSite)とJWT署名アルゴリズム(RS256 / EdDSA、none禁止)、Refresh Token Rotationの有無はコードに明示し、デフォルト任せにしないこと。認証方式そのもの(MFA・Passkey・IDaaS)は別カテゴリの担当で、本記事はセッションの器に徹します。
選定の優先順位をまとめると次の通りです。
- ドメイン構造で方式を決める ― 同一ドメインはCookie、横断はJWT
- Cookie属性を明示する ― HttpOnly + Secure + SameSite=Lax は最低ライン
- JWTは短命Access + Refresh Token Rotation ― 失効できない問題の標準解
- OAuthはAuthorization Code + PKCE一択 ― Implicit / Password Grant は選ばない
「同一ドメインはCookie、横断はJWT」迷ったらドメインで決めます。
まとめ
本記事は認証・セッション設計について、サーバセッション vs JWT・OAuth/OIDC・Cookie属性まで含めて解説しました。如何だったでしょうか。
同一ドメインはCookie、横断はJWT、外部連携はOIDC。Cookie属性は明示、JWTは短命+Refresh Token Rotation、OAuthはPKCE一択。これがソフトウェアアーキテクチャレベルでのセッション運用の現実解です。
これで「ソフトウェアアーキテクチャ」カテゴリ全8記事が完結しました。次回からは「アプリケーションアーキテクチャ」カテゴリに入り、クラス設計・ドメインロジック・命名規約・エラーハンドリングを解説していきます。
シリーズ目次に戻る → 『生成AI時代のアーキテクチャ超入門』の歩き方
それでは次の記事も閲覧いただけると幸いです。
📚 シリーズ:生成AI時代のアーキテクチャ超入門(24/89)