本記事について
当サイトを閲覧いただきありがとうございます。 本記事はシリーズ『生成AI時代のアーキテクチャ超入門』の「ソフトウェアアーキテクチャ」カテゴリ最終記事(第7弾)として、サーバ側の認証・セッションについて解説する記事です。
本記事の問いは「ログイン後のセッションをどう持つか」サーバセッション vs JWT・OAuth 2.0/OIDC・Cookie属性の安全設定まで扱い、「同一ドメインはCookie、横断はJWT」という実務判断軸を示します。
| 扱う | 扱わない(→別記事) |
|---|---|
| サーバセッション・JWT・OAuth・Cookieサーバ設定 | 認証強度(MFA / Passkey / IDaaS)・認可(IAM)・ブラウザ防御(XSS / CSP) |
本記事のテーマについてさらに詳しく知りたい方は『セキュア・バイ・デザイン』も参考にしてみてください。
そもそも認証・セッション設計とは何か
認証・セッション設計とは、ざっくり言えば「ログイン後のユーザーをどうやって覚えておくかの仕組みを決めること」です。
遊園地の入場を想像してください。入口でチケットを見せて本人確認する(認証)のは一度きりで、園内ではリストバンド(セッション)を見せるだけでアトラクションに乗れます。リストバンドをサーバー側で管理するか、来場者自身に持たせるか──この選択がサーバセッション方式とJWT方式の違いです。
なぜ認証・セッション設計が必要か
セッション管理の選択ミスは致命的なセキュリティホールになる
JWTの保管場所を間違えればXSSで盗まれ、Cookieの属性設定を忘れればCSRFで操作される──セッション周りの設計ミスは直接的な不正アクセスにつながります。
マイクロサービス化で認証の複雑度が激増する
モノリスでは1箇所で済んだセッション管理が、マイクロサービスでは全サービスにまたがります。最初に方式を統一しないと、サービスごとにバラバラな認証実装が乱立します。
AI時代にAPI経由のアクセスが急増する
AIエージェントがAPIを叩く時代では、人間のブラウザセッションとは別の認証フローが必要になります。両方を見据えた設計が求められます。
認証と認可の違い
アプリにログイン機能を実装するとき、しばしば混同されるのが認証(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やモバイルのような「秘密を保持できないクライアント」でも安全に使えます。
| フロー | 用途 |
|---|---|
| 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を防ぐ |
| 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必須 |
| JWTの失効は不可能と諦めてRotation未導入 | 短命Access Token + Refresh Token Rotationで事実上の即時失効が可能 |
| 認証と認可を同一設計として扱う | 「ログインできた=全権限あり」は典型的な混同。AuthN/AuthZは別設計が必須 |
認証方式そのもの(パスワード保存・MFA・Passkey)の鬼門は別カテゴリ「セキュリティアーキテクチャ」を参照してください。本記事はセッション技術の鬼門に限定しています。
AI判断軸
AI駆動開発が前提になると、セッション実装は「AIが正しく書ける領域」と「書かせてはいけない領域」がきっぱり分かれます。標準プロトコルに従う部分はAIが正確に書けますが、Cookie属性の明示・Refresh Token Rotation・失効APIのような運用設定は落としがちです。
| AI時代に有利 | AI時代に不利 |
|---|---|
| OAuth / OIDC 標準フローの実装 | 独自のトークン設計 |
| 短命Access Token + httpOnly Refresh Token | 長命・localStorage保存 |
| Cookie属性をコードで明示(HttpOnly等) | デフォルト任せ・暗黙の設定 |
| SDK呼び出し(Auth.js・Clerk SDK等) | 自前のセッション管理 |
選定の優先順位をまとめると次の通りです。
- ドメイン構造で方式を決める ― 同一ドメインはCookie、横断はJWT
- Cookie属性を明示する ― HttpOnly + Secure + SameSite=Lax は最低ライン
- JWTは短命Access + Refresh Token Rotation ― 失効できない問題の標準解
- OAuthはAuthorization Code + PKCE一択 ― Implicit / Password Grant は選ばない
AIが正しく書けるセッション実装と、落とす箇所
AIはOAuth 2.0 Authorization Code + PKCEのフロー全体を正確に書けます。RFC 7636に忠実なcode_verifierの生成、code_challengeの計算、認可エンドポイントへのリダイレクト、token エンドポイントへのPOST――この一連はほぼ間違えません。
問題は運用面の設定です。AIが生成したセッション関連コードでよく抜ける箇所を具体的に挙げます。
- Cookie属性の
SameSite=Laxが未設定(CSRF保護が効かない) - Refresh Tokenの有効期限やRotationの実装が省略される
- ログアウト時にサーバ側のRefresh Tokenを失効させるAPIが書かれない
HttpOnlyを付けずにJavaScriptからアクセス可能な状態で残る
これらはAIが「書けない」のではなく、明示的に指示しないと省略する箇所です。コードレビューやセキュリティテストのチェックリストで拾う前提の設計が必要です。
IDaaS SDK利用時のAI活用パターン
Auth.js(旧NextAuth)やClerk、Firebase Authのような認証SDKを使う場合、AIの生成精度は非常に高くなります。理由は単純で、これらのSDKはドキュメントが充実しており学習データに大量のサンプルが含まれているためです。
AIに認証周りの実装を任せる場合、自前のセッション管理を書かせるよりも、SDKのセットアップとミドルウェア設定を書かせる方が安全かつ正確です。Auth.jsの場合、providers 配列の設定、callbacks でのJWTカスタマイズ、ミドルウェアでの保護パス指定まで一気通貫で生成できます。
AI生成コードのセキュリティレビュー観点
AI が生成したセッション関連コードをレビューする際、以下の観点を必ず確認します。
- トークン保管場所 ― localStorageに入れていないか(XSS一発で全トークン流出)
- Cookie属性4点セット ― HttpOnly / Secure / SameSite / Path が全て明示されているか
- 失効経路の存在 ― ログアウト時にRefresh Tokenを無効化するAPI呼び出しがあるか
- PKCE対応 ― OAuthフローでcode_verifierを生成し、code_challengeをauthorizeリクエストに含めているか
これらは機能テストでは見つかりません。認証フローが動いていても、セキュリティ設定が甘い状態はテストをパスしてしまいます。静的解析やセキュリティスキャナーとの組み合わせが前提です。
「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選定・パスワードポリシー)は別カテゴリ「セキュリティアーキテクチャ」の認証設計記事の決定項目です。
この記事に関連する記事
https://senkohome.com/arch-intro-software-api/ https://senkohome.com/arch-intro-software-framework/ https://senkohome.com/arch-intro-software-language/
まとめ
本記事は認証・セッション設計について、サーバセッション vs JWT・OAuth/OIDC・Cookie属性まで含めて解説しました。如何だったでしょうか。
同一ドメインはCookie、横断はJWT、外部連携はOIDC。Cookie属性は明示、JWTは短命+Refresh Token Rotation、OAuthはPKCE一択。これがソフトウェアアーキテクチャレベルでのセッション運用の現実解です。
これで「ソフトウェアアーキテクチャ」カテゴリ全8記事が完結しました。次回からは「アプリケーションアーキテクチャ」カテゴリに入り、クラス設計・ドメインロジック・命名規約・エラーハンドリングを解説していきます。
シリーズ目次に戻る → 『生成AI時代のアーキテクチャ超入門』の歩き方
本記事で扱った内容の詳細は OAuth 2.0 も合わせて参考にしてください。
それでは次の記事も閲覧いただけると幸いです。
📚 シリーズ:生成AI時代のアーキテクチャ超入門(24/89)
