Skip to content

✍️ 필사 모드: OAuth 2.0 & OIDC Deep Dive — Authorization Code, PKCE, JWT, DPoP, FAPI 完全ガイド (2025)

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

TL;DR

  • OAuth 2.0は**認可(authorization)**プロトコルであり、認証(authentication)ではない。「このアプリに自分のGmailを読ませる」という委譲であって、「自分が誰であるか」の証明ではない。
  • OpenID Connect (OIDC)はOAuth 2.0の上に認証レイヤーを追加。ID Token (JWT)を発行して「ユーザーが誰であるか」を表現。
  • 4つのflow: Authorization Code (+ PKCE)、Client Credentials、Device Code、Refresh Token。ImplicitとPasswordはOAuth 2.1で廃止
  • PKCEは当初モバイル公開clientのための拡張だったが、OAuth 2.1ではすべてのclientで必須。Authorization Code傍受攻撃の防御の中核。
  • JWT: Base64URLエンコードされたheader.payload.signature。self-containedなトークン。検証は高速だが取り消しは困難。
  • OIDC Discovery: /.well-known/openid-configurationエンドポイントがすべてのURL/鍵/アルゴリズムを広告。clientが自動設定。
  • JWKS: 署名公開鍵をJSONで配布。鍵ローテーション可能。
  • Token binding: トークンを特定のclient/TLS接続に紐付ける。DPoP(proof-of-possession)、mTLS binding
  • FAPI: 金融グレードのセキュリティプロファイル。PAR、JARMなど追加保護。

1. OAuthの誕生

1.1 暗黒時代 (2000年代初頭)

2006年の方式:

アプリ: "Gmailのパスワードを教えてください。"
ユーザー: "..."
アプリ: GmailにIMAPログイン → 受信箱を読む。

問題点: アプリがパスワードを永久保存権限分離なし取り消し困難2FA不可

1.2 OAuth 1.0 (2007)

Twitter、Flickrなどが開発した権限委譲プロトコル。4つの役割: Resource Owner、Client、Resource Server、Authorization Server。ユーザーがASで承認→ASがclientにトークンを発行→clientはパスワードの代わりにトークンでAPIを呼ぶ。

しかしOAuth 1.0は複雑だった(リクエストごとにHMAC署名)。

1.3 OAuth 2.0 (2012)

RFC 6749。署名の代わりにTLS、トークンはbearer token、用途別に複数のflow。単純化の代償: セキュリティ責任が実装者に転嫁。正しく使えば安全だが、誤用しやすい

1.4 OIDC (2014)

OAuthは認可プロトコルだが、人々は認証に使った(「Facebookでログイン」)。問題: access tokenを受け取っても「このユーザーが本当にFacebookにログインした」という証明ではない

OpenID Connectが埋めた: OAuth 2.0の上にID Tokenを追加。署名されたJWTで「user@example.comが時刻TにAuth Serverにログインした」を証明。

1.5 OAuth 2.1 (ドラフト、2020+)

OAuth 2.0の10年の運用経験を反映:

  • Implicit flow削除: セキュリティ問題。
  • Password grant削除: clientがパスワードに触れること自体がOAuthに反する。
  • PKCE必須: すべてのflowで必要。
  • Redirect URI exact match: wildcard禁止。
  • Refresh token rotation: 使用ごとに交換。

2. OAuth 2.0の基本概念

2.1 4つの役割

  • Resource Owner (RO): リソースを所有するユーザー。
  • Client: リソースにアクセスしたいアプリ。
  • Resource Server (RS): APIサーバー。
  • Authorization Server (AS): トークン発行サーバー。

RSとASは同じ組織の場合も別の場合もある。Googleは両方運用、Auth0はASのみ提供。

2.2 Clientの種類

  • Confidential Client: 秘密を安全に保管できる(サーバーサイドWebアプリ)。Client Secret使用可能。
  • Public Client: 保管できない(モバイルアプリ、SPA、CLI)。Secretなし → PKCEで補完。

OAuth 2.1ではほぼ全ケースでPKCEを使用。

2.3 トークンの種類

  • Access Token: RSへのアクセス。短命(5〜60分)。
  • Refresh Token: Access Token再発行用。長命(数日〜数ヶ月)。ASにのみ提出。
  • ID Token (OIDCのみ): ユーザー身元情報。JWT。Clientが直接解析・検証。

2.4 Scope

権限範囲。scope=read:email write:calendar profile openidopenid scopeはOIDC使用を意味し、ID Tokenも発行される。


3. Authorization Code Flow

最も重要なflow。WebアプリのLogin標準。

3.1 概要

User ←→ Client ←→ Authorization Server ←→ Resource Server

1. User: "アプリでログインして"
2. Client: UserをASにリダイレクト
3. AS: ログイン画面表示
4. User: ログイン + 承認
5. AS: authorization codeをclientに返す (redirect)
6. Client: codeをtokenに交換 (サーバー間直接通信)
7. Client: access tokenでRS呼び出し

3.2 詳細フロー

Authorization Request:

GET /authorize?
    response_type=code
    &client_id=abc123
    &redirect_uri=https://app.example.com/callback
    &scope=openid profile email
    &state=xyz789
    &code_challenge=<PKCE>
    &code_challenge_method=S256

Authorization Response:

https://app.example.com/callback?
    code=AUTH_CODE_XYZ
    &state=xyz789

Clientはstateを検証(CSRF防御)。

Token Request:

POST /token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTH_CODE_XYZ
&redirect_uri=https://app.example.com/callback
&client_id=abc123
&client_secret=secret456
&code_verifier=<PKCE>

Token Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def456...",
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "scope": "openid profile email"
}

API呼び出し:

GET /api/userinfo
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

3.3 なぜこれほど複雑なのか

Front-channel/Back-channel分離: codeはブラウザ経由、tokenはサーバー間直接HTTP。tokenがブラウザに露出しない。ユーザー対話分離: ログイン・同意はAS画面、token使用はclientサーバー処理。Clientはユーザーパスワードを知る必要がない。


4. PKCE — 現代OAuthの必須

4.1 問題

モバイルアプリやSPAはclient secretを安全に保管できない。バイナリをデコンパイルすればsecretが露出。

攻撃シナリオ: 悪意のあるアプリが同じcustom URL schemeを登録 → AS authorization codeのredirectを傍受 → codeをtokenに交換 → アカウント乗っ取り。Secretがないためclientを証明できない。

4.2 PKCEの解決

PKCE (Proof Key for Code Exchange, RFC 7636)。発音: "ピクシー(pixy)"。

アイデア: clientがランダムな秘密値を生成し、(1) ハッシュを事前にASに送信、(2) token要求時に元の値を証明。

4.3 動作

  1. code_verifier生成 (43〜128文字ランダム)。
  2. code_challenge = BASE64URL(SHA256(code_verifier))
  3. Authorization requestにcode_challenge含める。
  4. ASがcode発行時にcode_challenge記憶
  5. Token requestに元のcode_verifier含める。
  6. AS検証: SHA256(code_verifier) == code_challenge?

一致すればtoken発行。

4.4 攻撃防御

悪意のあるアプリがcodeを傍受してもcode_verifierを知らない(元は合法なアプリのメモリ内)。Token要求が失敗。

4.5 Plain vs S256

  • plain: ハッシュなし(使用禁止)。
  • S256: SHA256(code_verifier)(必須)。

4.6 OAuth 2.1: すべてのclientで必須

Defense in depth: secretが漏洩してもPKCEが追加防御線。


5. JWT — JSON Web Token

5.1 構造

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
HeaderPayloadSignature

各部分はBase64URLエンコードされたJSON。

Header:

{ "alg": "HS256", "typ": "JWT", "kid": "my-key-id" }

Payload (Claims):

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1700000000,
  "exp": 1700003600,
  "iss": "https://example.com",
  "aud": "my-api"
}

標準claim: sub(Subject)、iss(Issuer)、aud(Audience)、exp(Expiration)、iat(Issued At)、nbf(Not Before)、jti(JWT ID)。

5.2 署名アルゴリズム

  • HS256: HMAC-SHA256。対称鍵。
  • RS256: RSA-SHA256。非対称。
  • ES256: ECDSA P-256。非対称、より小さい署名。
  • EdDSA: Ed25519。最速・最新。
  • none: 本番絶対禁止。

OIDCは通常**非対称(RS256/ES256)**を使用 — ASが署名、client/RSが公開鍵で検証。

5.3 "alg: none"脆弱性

初期のJWTライブラリが{"alg":"none"}.{"admin":true}.のような署名なしJWTを受け入れた。対策: 検証時に期待するアルゴリズムをハードコードalgを信頼しない。

5.4 Key Confusion

サーバーがRS256の公開鍵を持つ。攻撃者がalg: HS256に変更し、公開鍵をHMAC秘密として署名。ライブラリがalgを信頼すると偽造通過。対策: アルゴリズム固定。

5.5 JWTの長所と短所

長所: self-contained、stateless、標準化。短所: 取り消し困難(expまで有効)、サイズ(毎リクエスト送信)、payloadは署名されるが暗号化されない(誰でも見える)。

実務: 短命access token (5〜15分) + refresh token


6. OIDC — 認証レイヤー

6.1 OAuthとの違い

OAuthは認可、OIDCは認証。OAuthだけだとaccess tokenがあっても「誰がログインしたか」公式には分からない。OIDCの解決: ID Token

6.2 ID Token vs Access Token

項目ID TokenAccess Token
目的ユーザー身元証明APIアクセス
受信者ClientResource Server
形式JWT (必須)JWT または opaque
誰が検証ClientResource Server
転送先Clientメモリ (HTTP要求に載せない)Authorizationヘッダー

重要: ID TokenをAPI呼び出しに使うな。Access Tokenを身元確認に使うな。

6.3 ID Token例

{
  "iss": "https://accounts.google.com",
  "sub": "110169484474386276334",
  "aud": "1234.apps.googleusercontent.com",
  "iat": 1700000000,
  "exp": 1700003600,
  "email": "user@example.com",
  "email_verified": true,
  "name": "John Doe"
}

6.4 Nonce

ID token再生攻撃防止。Clientがauthorization requestにnonce=<random>を追加 → ASがID Tokenのnonce claimに同じ値を含める → Client検証時に一致確認。

6.5 UserInfo Endpoint

GET /userinfo
Authorization: Bearer <access_token>

7. OIDC DiscoveryとJWKS

7.1 Discovery

GET https://accounts.google.com/.well-known/openid-configuration

レスポンス:

{
  "issuer": "https://accounts.google.com",
  "authorization_endpoint": "...",
  "token_endpoint": "...",
  "userinfo_endpoint": "...",
  "jwks_uri": "...",
  "id_token_signing_alg_values_supported": ["RS256"]
}

Clientはissuer URLだけ知っていればすべて自動設定。

7.2 JWKS

{
  "keys": [
    { "kid": "key-1", "kty": "RSA", "alg": "RS256", "use": "sig", "n": "...", "e": "AQAB" }
  ]
}

検証者はJWT headerのkidでキーを特定し、そのキーで署名検証。

7.3 Key Rotation

Day 1:  [A]
Day 30: [A, B]   # B追加、Aで署名された既存トークンも検証可能
Day 60: [B]      # A削除

ClientはJWKSを定期的にリフレッシュし、未知のkidに対応。


8. Client Credentials Flow

サーバー間通信用。ユーザーなし。

POST /token
Authorization: Basic <client_id:client_secret base64>
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&scope=read:users

ID Tokenなし。Secretの管理(Vault、Secrets Manager)、mTLS推奨、短命トークン。


9. Device Code Flow

TV、ゲーム機、CLIなどキーボード入力が困難なデバイス用。

POST /device/code
client_id=abc123
scope=openid profile

レスポンス:

{
  "device_code": "SOME_LONG_CODE",
  "user_code": "WDJB-MJHT",
  "verification_uri": "https://example.com/device",
  "expires_in": 1800,
  "interval": 5
}

デバイスはURLとcodeを表示 → ユーザーがスマホでURLにアクセスしcodeを入力 → デバイスは/tokenをpolling。Google TV、GitHub CLI、gcloud auth loginで使用。


10. Refresh Token Flow

POST /token
grant_type=refresh_token
&refresh_token=OLD_REFRESH
&client_id=abc123

10.1 Rotation

OAuth 2.1推奨: refresh tokenを毎回交換

  • 盗まれたrefresh tokenが1回しか使えない。再使用をASが検知 → セッション全体無効化。
  • 欠点: race condition(同時refresh要求)処理が必要。

10.2 保管

  • Webアプリ: HTTPOnly、Secureクッキー。
  • モバイル: OS keychain (iOS Keychain、Android Keystore)。
  • SPA: 保管不可。PKCE + 短命access tokenのみ推奨。

11. Webアプリのセキュリティ

11.1 Redirect URI検証

ASは登録済みredirect_uriと完全一致するURLにのみリダイレクト。Wildcard禁止(OAuth 2.1)。

11.2 State

CSRF防御。ランダム値を認証要求に含め、レスポンスで一致確認。Stateなしだと攻撃者が自分のauthorization codeを被害者に注入できる。

11.3 Token Storage (SPA)

  • LocalStorage: XSS脆弱。
  • Memoryのみ: リロードで消失。
  • HTTPOnlyクッキー: JSアクセス不可、CSRFは別途対策。

現実: 完璧な方法はない。ベストプラクティスは短命access token + メモリ保存、またはBackend-for-Frontendパターン(サーバーがトークン保管、ブラウザはセッションクッキーのみ)。

11.4 Implicit Flowが廃止された理由

トークンをURL fragmentで直接返す: ブラウザ履歴に残る、referrerで漏洩、JS経由でアクセス可能、refresh tokenなし。Authorization Code + PKCEが標準に。

11.5 Open Redirect

redirect_uriが厳密に検証されないと、ログイン後に悪意のあるサイトへリダイレクト → フィッシング。対策: 許可リストによる完全一致。


12. Token Binding: DPoPとmTLS

Bearer tokenの問題: トークンを盗めばどのclientでも使える

12.1 mTLS Client Certificate

RFC 8705。ClientがTLS接続でclient certificateを使用。ASはトークンにcertificate thumbprintをバインド:

{ "cnf": { "x5t#S256": "SHA256(client_cert)" } }

RSは要求時にTLS接続のclient certを確認。

12.2 DPoP

RFC 9449。Clientが別の公開鍵ペアを生成し(セッションごと)、各要求に署名。

Step 1: Token requestにDPoPヘッダー追加。

Step 2: ASがトークンにDPoP thumbprintバインド:

{ "cnf": { "jkt": "SHA256(DPoP public key)" } }

Step 3: API呼び出し時に毎回DPoP JWS生成:

GET /api/users
Authorization: DPoP <access_token>
DPoP: <new JWS for this request>

RSは検証: (1) DPoP JWS署名、(2) cnf.jktと公開鍵一致、(3) htm/htu claimが実要求と一致。

12.3 Bearer vs DPoP

項目BearerDPoP
トークン盗難時攻撃者が使用可能使用不可 (private keyなし)
Client複雑度高 (毎要求署名)
採用率非常に高い成長中 (金融、ヘルスケア)

13. FAPI — Financial-grade API

金融機関のopen banking用プロファイル。

13.1 FAPI 1.0

  • Baseline (読み取り): PKCE + state/nonce必須。
  • Advanced (読み書き): PAR (Pushed Authorization Request)、JARM (JWT Response)、mTLSまたはDPoPによるtoken binding、署名されたRequest Object

13.2 PAR

通常のOAuth:

GET /authorize?response_type=code&...&scope=...&state=...

パラメータがURLに露出、改ざんリスク。

PAR:

POST /par
<request object>

Response: { "request_uri": "urn:request:xyz" }

その後:

GET /authorize?client_id=...&request_uri=urn:request:xyz

URLが短く安全、署名されたrequest object。

13.3 FAPI 2.0

2024年以降進行中。よりシンプルで強力: PAR + DPoPデフォルト、sender-constrained tokens強制。英国、豪州、ブラジルのopen bankingで採用。


14. 実プロバイダー比較

  • Auth0: 機能豊富、優れたドキュメント、高価、vendor lock-in。B2C/SaaS向け。
  • Okta: エンタープライズ最強、SSO、ガバナンス。高価で複雑。
  • Keycloak: オープンソース、セルフホスト可能、運用負担大。
  • AWS Cognito: AWS統合、低価格、カスタマイズ制限。
  • Google / Apple / GitHub: Social login、OIDC対応。
  • Ory (Hydra/Kratos/Keto): クラウドネイティブなオープンソース代替。

15. よくあるセキュリティミス

Open Redirect — whitelistで解決。

Access Tokenで認証 — 誤り。OIDC ID TokenまたはUserInfo endpointを使う。

Refresh TokenをLocalStorageに — XSSで漏洩。HTTPOnlyクッキーまたはBFFパターン。

Scope過度要求 — 最小scopeのみ要求。

JWT検証省略 — 署名検証なしにpayloadを信頼すると誰でも偽造可能。jsonwebtokenjoseなどの検証ライブラリ使用。

exp検証漏れ — 必ずexp検証。


16. デバッグツール

  • jwt.io — JWTデコーダー/検証器。本番トークンを貼らない
  • Postman / Insomnia — OAuth flowテスト。
  • oidcdebugger.com — authorization request生成と検査。
  • MITM Proxy / Charles — HTTPトラフィック傍受。
  • OpenID Connect Conformance Test Suite — 公式準拠テスト。

17. 要約 — チートシート

┌─────────────────────────────────────────────────────┐
OAuth 2.0 / OIDC Cheat Sheet├─────────────────────────────────────────────────────┤
Roles: RO, Client, RS, AS│                                                       │
Flows (2.1):1. Authorization Code + PKCE (Web/Mobile/SPA)2. Client Credentials (server-to-server)3. Device Code (TV/CLI)4. Refresh TokenDeprecated: Implicit, Password│                                                       │
Tokens:Access: API access, short-lived                    │
Refresh: renewal, long-lived, rotate               │
ID: identity (JWT, OIDC only)│                                                       │
JWT: header.payload.signature (Base64URL)│   alg=RS256/ES256/EdDSA (never none)│                                                       │
PKCE: verifier → S256 challenge; S256 only           │
OIDC Discovery: /.well-known/openid-configuration    │
JWKS: kid-based key rotation                         │
│                                                       │
Security: state, nonce, exact redirect_uri, HTTPS,│           short access tokens, PKCE always           │
│                                                       │
Token binding: Bearer (default) / mTLS / DPoPFAPI: PAR, JARM, mTLS or DPoP required               │
└─────────────────────────────────────────────────────┘

18. クイズ

Q1. OAuth 2.0とOIDCの違いは?

A. OAuth 2.0は認可プロトコルOIDCは認証プロトコル。OAuthは「このアプリが私のGmailを読むことを許可する」という委譲。OIDCは「このユーザーが今ログインした」という身元確認。技術的にはOIDCはOAuth 2.0の上にID Token (JWT)を追加したレイヤー。OAuthを認証に使うのはよくあるミスで、OAuthバグの90%はここから。

Q2. PKCEが解決する攻撃は?

A. Authorization Code Interception Attack。モバイルアプリやSPAはclient secretを安全に保管できず、authorization codeを傍受した悪意のあるアプリがtokenに交換可能だった。PKCEはclientがランダムなcode_verifierを作り、code_challenge = SHA256(verifier)をauthorization requestに送信、token request時に元のverifierを証明。途中でcodeを盗んでもverifierを知らなければtoken交換不可。OAuth 2.1ではすべてのclientで必須。

Q3. JWTのalg: none脆弱性とは?

A. JWT headerのalg値がnoneだと署名なし。初期のJWTライブラリがこれを受け入れ、攻撃者が{"alg":"none"}.{"admin":true}.のようなpayloadを作れば誰でも偽造可能だった。防御: 検証時に期待するアルゴリズムをハードコードし、algを信頼しない。関連脆弱性: HS/RS Key Confusion — サーバーがRS256公開鍵を持つ時、攻撃者がalg:HS256に変更し公開鍵をHMAC秘密として偽造。

Q4. OIDC DiscoveryとJWKSが解決する問題は?

A. 設定自動化と鍵ローテーション。Discovery (/.well-known/openid-configuration)はASのすべてのエンドポイント、アルゴリズム、scopeを1つのJSONで公開 → clientはissuer URLだけ知っていれば自動設定。JWKS (/.well-known/jwks.json)は署名公開鍵を配布し、各鍵にkidがあるためASが複数鍵を同時公開可能。ASが鍵を交換する時[A, B]状態を経て[B]に移行すれば既存トークンも検証可能。

Q5. Implicit Flowが廃止された理由は?

A. Access tokenがブラウザに露出するセキュリティ問題。Implicit flowはトークンをURL fragment (#access_token=...)で直接返す。問題: ブラウザ履歴に保存、refererヘッダーで漏洩、JavaScriptからアクセス可能、refresh tokenなし。Authorization Code + PKCEがはるかに安全でrefresh可能。OAuth 2.1で公式廃止。

Q6. DPoPは何を解決するか?

A. Bearer tokenの盗難リスク。通常のbearer tokenはトークン値を持つ誰でも使用可能。DPoP (Demonstrating Proof-of-Possession)はclientがセッションごとに鍵ペアを作り、毎API要求にDPoP JWSをprivate keyで署名して送信。ASはtoken発行時にDPoP公開鍵thumbprintをtokenにバインド (cnf.jkt)。RSは要求時に(1) tokenのjkt、(2) 現在のDPoP公開鍵hash、(3) 要求URL/methodを一致確認。盗まれたtokenもprivate keyなしでは使用不可。

Q7. Refresh token rotationとは何であり、なぜ必要か?

A. Refresh tokenを使用するたびに新しい値に交換し、以前の値を無効化すること。Refresh tokenは長期有効なので盗難リスクが大きい。Rotation ありなら攻撃者が一度使えば合法ユーザーのrefresh tokenが無効化、次のrefresh試行が失敗 → セッション乗っ取り検知。ASは「すでに使用されたrefresh tokenが再提出された」を検知したらトークンファミリー全体を無効化し、攻撃者と被害者両方のセッション終了。OAuth 2.1で強く推奨。

현재 단락 (1/310)

- **OAuth 2.0**は**認可(authorization)**プロトコルであり、認証(authentication)ではない。「このアプリに自分のGmailを読ませる」という委譲であって、...

작성 글자: 0원문 글자: 13,834작성 단락: 0/310