目次

読了時間: 約 15 分 | 文字数: 約 5,500 字

「認証(Authentication)は『あなたは誰か』、認可(Authorization)は『あなたは何をしてよいか』」——この 2 つは混同されやすいが、全く異なる概念だ。本稿では、現代の Web システムで必須となる認証・認可プロトコル(OAuth2、OpenID Connect、JWT、SAML)の仕組みと、適切な使い分けを解説する。

認証 vs 認可

認証(Authentication)

「あなたは誰か」を確認するプロセス

認証の例:
- ユーザー名 + パスワード入力
- 指紋認証
- スマートフォンの生体認証
- 認証アプリの 6 桁コード

出力:「このユーザーは user_id: 123 である」

認可(Authorization)

「あなたは何をしてよいか」を決定するプロセス

認可の例:
- 管理者は全ユーザーデータを閲覧可能
- 一般ユーザーは自分のデータのみ編集可能
- 無料プランは機能 A のみ利用可能
- 有料プランは全機能利用可能

出力:「user_id: 123 は /admin エンドポイントにアクセスできない」

両者の関係

1. 認証:ユーザーが誰かを確認
        ↓
2. 認可:そのユーザーの権限を確認
        ↓
3. アクセス:リソースへのアクセスを許可/拒否

セッションとトークン

セッションベース認証(伝統的)

サーバー側で状態を管理

フロー:
1. ユーザーがログイン
2. サーバーがセッション ID を生成し DB に保存
3. サーバーがセッション ID を Cookie に設定
4. 以降のリクエストは Cookie を自動送信
5. サーバーがセッション ID で DB 照会

メリット:
- サーバー側で即時無効化可能
- 実装が単純

デメリット:
- サーバーに状態が必要(スケーラビリティ課題)
- CORS・クロスドメインに弱い

トークンベース認証(現代的)

クライアント側でトークンを保持

フロー:
1. ユーザーがログイン
2. サーバーが JWT を生成・署名
3. クライアントが JWT を保存(localStorage 等)
4. 以降のリクエストは Authorization ヘッダーに添付
5. サーバーが署名を検証(DB 不要)

メリット:
- ステートレス(スケーラブル)
- CORS・クロスドメイン対応容易
- モバイル・SPA に最適

デメリット:
- 即時無効化が困難(TTL 管理が必要)
- トークン漏洩リスク

JWT(JSON Web Token)

JWT の構造

3 つの部分から構成されるトークン

形式:
xxxxx.yyyyy.zzzzz
  ↓       ↓       ↓
Header  Payload  Signature

例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

各パートの詳細

// 1. Header(アルゴリズムとトークンタイプ)
{
  "alg": "HS256",
  "typ": "JWT"
}

// 2. Payload(クレーム=主張)
{
  "sub": "1234567890",      // 主体(ユーザー ID)
  "name": "John Doe",       // ユーザー名
  "iat": 1516239022,        // 発行時刻
  "exp": 1516242622,        // 有効期限
  "scope": "read write",    // 権限
  "role": "admin"           // ロール
}

// 3. Signature(署名)
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

JWT の検証

// Node.js 例(jsonwebtoken ライブラリ)
const jwt = require('jsonwebtoken');

// 生成
const token = jwt.sign(
  { userId: 123, role: 'admin' },
  'your-secret-key',
  { expiresIn: '1h' }
);

// 検証
try {
  const decoded = jwt.verify(token, 'your-secret-key');
  console.log(decoded); // { userId: 123, role: 'admin', iat: ..., exp: ... }
} catch (err) {
  // 無効なトークン
  console.error('Invalid token');
}

JWT ベストプラクティス

項目推奨
署名アルゴリズムRS256(非対称)を優先
有効期限短め(15 分〜1 時間)
機密情報JWT に含めない(暗号化されてない)
保管場所httpOnly Cookie が安全
無効化リフレッシュトークン+ブラックリスト

OAuth2——認可のプロトコル

OAuth2 の基本

サードパーティアプリに権限委譲するためのプロトコル

 OAuth2 の役割:
「Google アカウントでログイン」
「このアプリに Twitter 投稿を許可」
「Slack チャンネル履歴へのアクセスを許可」

OAuth2 の登場人物

┌─────────────┐     ┌─────────────┐
│   リソース   │     │  認証サーバー │
│    オwner    │ ←─→ │ (Authorization│
│  (ユーザー)  │     │    Server)   │
└──────┬──────┘     └──────┬──────┘
       │                   │
       │                   │
┌──────▼──────┐     ┌──────▼──────┐
│   クライ     │ ←─→ │    リソース   │
│    アント    │     │    サーバー   │
│  (アプリ)   │     │   (API)     │
└─────────────┘     └─────────────┘
登場人物説明
リソースオーナーリソースへのアクセス権を持つユーザー
クライアントリソースにアクセスするアプリWeb アプリ、モバイルアプリ
リソースサーバー保護されたリソースを提供API サーバー
認証サーバー認証とトークン発行を担当Auth0, Cognito, Okta

認可コードフロー(最も一般的)

1. ユーザーがアプリで「Google でログイン」をクリック
                ↓
2. アプリが Google 認証ページにリダイレクト
   GET https://accounts.google.com/o/oauth2/auth?
     client_id=xxx&
     redirect_uri=https://myapp.com/callback&
     response_type=code&
     scope=email profile
                ↓
3. ユーザーが Google でログイン・許可
                ↓
4. Google が認可コードを付与してリダイレクト
   https://myapp.com/callback?code=AUTH_CODE
                ↓
5. アプリが認可コードをアクセストークンに交換
   POST https://oauth2.googleapis.com/token
   {
     code: AUTH_CODE,
     client_id: xxx,
     client_secret: yyy,
     redirect_uri: https://myapp.com/callback,
     grant_type: 'authorization_code'
   }
                ↓
6. アクセストークン取得
   {
     "access_token": "ya29.xxx",
     "token_type": "Bearer",
     "expires_in": 3600,
     "refresh_token": "1//xxx"
   }
                ↓
7. アクセストークンで API アクセス
   GET https://www.googleapis.com/oauth2/v2/userinfo
   Authorization: Bearer ya29.xxx

OAuth2 グラントタイプ

グラント用途セキュリティ
認可コードWeb アプリ◎(最も安全)
PKCESPA・モバイル◎(認可コード+追加検証)
クライアントクレデンシャルサーバー間通信○(アプリ認証のみ)
リソースオーナーパスワード非推奨✕(パスワード預かり)
インプリシット非推奨(削除)✕(トークン漏洩リスク)

PKCE(Proof Key for Code Exchange)

モバイル・SPA 向けのセキュリティ拡張

従来の認可コードフローの問題:
- モバイルアプリの URI スキームハイジャック
- 認可コードの傍受・なりすまし

PKCE の解決策:
1. クライアントが code_verifier(ランダム文字列)を生成
2. code_verifier から code_challenge を生成(ハッシュ)
3. 認証リクエストに code_challenge を追加
4. トークン交換時に code_verifier を送信
5. 認証サーバーが code_challenge と照合

OpenID Connect(OIDC)——認証のプロトコル

OAuth2 と OIDC の関係

OAuth2: 認可プロトコル(「何をしてよいか」)
        ↓
  拡張
        ↓
OIDC: 認証プロトコル(「あなたは誰か」)

OIDC の追加要素:

  • ID トークン: ユーザー情報を JWT で返す
  • UserInfo エンドポイント: 追加プロフィール取得
  • 標準スコープ: openid, email, profile, address, phone

OIDC フロー

1-6. OAuth2 認可コードフローと同じ
                ↓
7. ID トークン取得(アクセストークンと同時)
   {
     "access_token": "xxx",
     "id_token": "eyJhbGc...",  // JWT
     "token_type": "Bearer",
     "expires_in": 3600
   }
                ↓
8. ID トークンをデコード・検証
   {
     "iss": "https://accounts.google.com",
     "sub": "1234567890",
     "aud": "my-client-id",
     "exp": 1516242622,
     "email": "user@example.com",
     "name": "John Doe",
     "picture": "https://..."
   }
                ↓
9. ユーザーログイン完了

ID トークンの検証項目

// 必須検証項目
1. 署名の検証発行元の公開鍵で
2. iss クレーム発行元が信頼できるか
3. aud クレーム自社の client_id 
4. exp クレーム有効期限切れでないか
5. nonce クレームリプレイアタック防止

SAML——エンタープライズ標準

SAML の基本

企業向けシングルサインオン(SSO)プロトコル

OAuth2/OIDC vs SAML:

OIDC:
- 2014 年、比較的新しい
- REST/JSON ベース
- モバイル・SPA 向け
- Google, Facebook 等が採用

SAML:
- 2005 年、歴史ある
- XML ベース
- 企業システム向け
- Microsoft AD, Okta 等が採用

SAML フロー

1. ユーザーが SaaS アプリにアクセス
                ↓
2. アプリが IdP(認証プロバイダー)にリダイレクト
                ↓
3. 企業が提供する IdP で認証(AD 等)
                ↓
4. IdP が SAML アサーション(XML)を生成・署名
                ↓
5. ブラウザ経由で SaaS アプリに返却
                ↓
6. アプリが署名を検証・ログイン完了

SAML の構成要素

<!-- SAML アサーション(簡略版) -->
<saml:Assertion>
  <saml:Subject>
    <saml:NameID>user@company.com</saml:NameID>
  </saml:Subject>
  <saml:Conditions>
    <saml:AudienceRestriction>
      <saml:Audience>saas-app</saml:Audience>
    </saml:AudienceRestriction>
  </saml:Conditions>
  <saml:AttributeStatement>
    <saml:Attribute Name="email">
      <saml:AttributeValue>user@company.com</saml:AttributeValue>
    </saml:Attribute>
    <saml:Attribute Name="role">
      <saml:AttributeValue>admin</saml:AttributeValue>
    </saml:Attribute>
  </saml:AttributeStatement>
</saml:Assertion>

プロトコルの選択指針

ユースケース別推奨

ユースケース推奨プロトコル理由
一般向け Web アプリOIDC簡単、モバイル対応、情報豊富
モバイルアプリOIDC + PKCEセキュア、ネイティブ対応
SPA(React/Vue)OIDC + PKCEブラウザセキュリティ対応
企業向け SaaSSAML + OIDC企業は SAML 必須、一般は OIDC
サーバー間 APIクライアントクレデンシャルシンプル、効率的
マイクロサービスJWT + mTLSステートレス、相互認証

認証プロバイダーの選択

プロバイダー特徴向いているケース
Auth0多機能、開発者体験重視スタートアップ〜中堅
AWS CognitoAWS 統合、コスト安AWS 環境
Okta企業機能、SAML 強化エンタープライズ
Azure ADMicrosoft 環境統合企業、Office 365 利用者
自前構築Keycloak, Oryコスト重視、カスタマイズ

セキュリティベストプラクティス

パスワード保管

// ❌ NG: フラットテキスト
db.users.password = "password123"

// ❌ NG: MD5, SHA1(速すぎて総当たり可能)
db.users.password = md5("password123")

// ○ OK: bcrypt, scrypt, Argon2(意図的に遅い)
db.users.password = bcrypt.hash("password123", saltRounds=12)

マルチファクター認証(MFA)

必須の現代標準

要素強度
知識パスワード、PIN
所持スマホ、セキュリティキー
生体指紋、顔認証

推奨: パスワード+認証アプリ(TOTP)またはセキュリティキー(FIDO2)

セッション管理

推奨設定:
- アクセストークン TTL: 15 分〜1 時間
- リフレッシュトークン TTL: 1 日〜30 日
- 絶対セッションタイムアウト: 24 時間
- 不審なアクティビティ検知: 位置情報、デバイス指紋

So What?——実務への応用

  • OIDC をデフォルトに: 新規プロジェクトは OIDC で間違いない
  • PKCE を必ず実装: モバイル・SPA は必須
  • JWT は短寿命に: 1 時間以内、リフレッシュトークン併用
  • 機密情報は JWT に含めない: 平気デコード可能
  • MFA は必須機能に: パスワードのみは危険
  • 認証はプロバイダー活用: 自前実装はリスク大

認証・認可はセキュリティの要。間接的な実装は、取り返しのつかない侵害につながる。プロトコルの仕組みを理解し、ベストプラクティスに従うことが必須だ。

参考リンク

免責事項 — 掲載情報は執筆時点のものです。料金・機能は変更される場合があります。最新情報は各公式サイトをご確認ください。