目次
読了時間: 約 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 アプリ | ◎(最も安全) |
| PKCE | SPA・モバイル | ◎(認可コード+追加検証) |
| クライアントクレデンシャル | サーバー間通信 | ○(アプリ認証のみ) |
| リソースオーナーパスワード | 非推奨 | ✕(パスワード預かり) |
| インプリシット | 非推奨(削除) | ✕(トークン漏洩リスク) |
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 | ブラウザセキュリティ対応 |
| 企業向け SaaS | SAML + OIDC | 企業は SAML 必須、一般は OIDC |
| サーバー間 API | クライアントクレデンシャル | シンプル、効率的 |
| マイクロサービス | JWT + mTLS | ステートレス、相互認証 |
認証プロバイダーの選択
| プロバイダー | 特徴 | 向いているケース |
|---|---|---|
| Auth0 | 多機能、開発者体験重視 | スタートアップ〜中堅 |
| AWS Cognito | AWS 統合、コスト安 | AWS 環境 |
| Okta | 企業機能、SAML 強化 | エンタープライズ |
| Azure AD | Microsoft 環境統合 | 企業、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 は必須機能に: パスワードのみは危険
- 認証はプロバイダー活用: 自前実装はリスク大
認証・認可はセキュリティの要。間接的な実装は、取り返しのつかない侵害につながる。プロトコルの仕組みを理解し、ベストプラクティスに従うことが必須だ。
参考リンク
- OAuth 2.0 RFC 6749 — OAuth2 仕様
- OpenID Connect Core — OIDC 仕様
- JWT RFC 7519 — JWT 仕様
- Auth0 ブログ — 認証・認可の最新動向
免責事項 — 掲載情報は執筆時点のものです。料金・機能は変更される場合があります。最新情報は各公式サイトをご確認ください。