110 lines
9.8 KiB
Markdown
110 lines
9.8 KiB
Markdown
---
|
|
title: RFC 7636 — Proof Key for Code Exchange by OAuth Public Clients (PKCE)
|
|
source_type: official-doc
|
|
url: https://datatracker.ietf.org/doc/html/rfc7636
|
|
archive_url:
|
|
status: raw
|
|
confidence: high
|
|
tags: [keycloak-patterns, p2a-spa-resource-server, p3a-single-ec2, vanilla-js, oauth2, pkce, public-client, ietf-rfc]
|
|
related_projects: [keycloak-patterns]
|
|
related_branches: [feature-keycloak-patterns, feature-keycloak-internal-spa-direct-no-google, feature-keycloak-single-ec2-no-google, feature-keycloak-vanilla-js-spa-pkce, feature-keycloak-pkce-flow-stages]
|
|
created: 2026-05-25
|
|
last_reviewed: 2026-05-27
|
|
---
|
|
|
|
# RFC 7636 — Proof Key for Code Exchange (PKCE)
|
|
|
|
> Layer: `raw/official-docs/` — IETF RFC 7636 (Standards Track) 발췌. PKCE 메커니즘의 표준 정의.
|
|
|
|
## Parent / 활용 branch (필수)
|
|
|
|
| Branch | 이 자료가 정당화하는 결정 |
|
|
|---|---|
|
|
| [[raw/branch-notes/feature-keycloak-patterns]] | keycloak-patterns root에서 SPA/native 등 public client 패턴이 PKCE를 의무로 채택하는 표준 근거 |
|
|
| [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]] | P2A SPA Direct에서 client secret 없는 public client가 PKCE로 code interception을 방어하는 근거 |
|
|
| [[raw/branch-notes/feature-keycloak-single-ec2-no-google]] | P3A 단일 EC2 vanilla JS 구현에서 PKCE flow가 baseline인 표준 근거 |
|
|
| [[raw/branch-notes/feature-keycloak-vanilla-js-spa-pkce]] | vanilla JS SPA의 code_verifier 생성 / S256 challenge 변환 구현 근거 |
|
|
| [[raw/branch-notes/feature-keycloak-pkce-flow-stages]] | PKCE flow의 (A)~(E) 단계별 분해와 verifier-challenge chain 설계 근거 |
|
|
|
|
## 컨텍스트
|
|
|
|
P2A (Internal SPA + Resource Server) 패턴의 SPA는 **public client** — client secret을 안전하게 보관할 방법이 없음. 따라서 authorization code 탈취 시 즉시 token 교환이 가능해지는 공격 surface를 막기 위해 PKCE가 의무. "왜 SPA에 PKCE를 강제로 켜야 하나"의 1차 근거.
|
|
|
|
## 출처 / Source
|
|
|
|
- 원본 URL: https://datatracker.ietf.org/doc/html/rfc7636
|
|
- 아카이브 URL: (미수집)
|
|
- 저자 / 조직: IETF — N. Sakimura (Nomura Research Institute), J. Bradley (Ping Identity), N. Agarwal (Google)
|
|
- 발행일: 2015-09 (RFC 7636 Standards Track)
|
|
- 관련: RFC 6749 (OAuth 2.0 Core), RFC 8252 (OAuth 2.0 for Native Apps), OAuth 2.1 draft
|
|
- 마지막 확인일: 2026-05-27 (WebFetch 재검증 완료 — 5개 quote 중 4개 verbatim MATCH, 1개는 quote 스타일 차이만 있음)
|
|
|
|
## 핵심 인용 / Key quotes (verbatim)
|
|
|
|
> [§1 Abstract/Introduction, 2026-05-27 verified MATCH] "OAuth 2.0 public clients utilizing the Authorization Code Grant are susceptible to the authorization code interception attack."
|
|
|
|
> [§1.1 Protocol Flow, 2026-05-25 capture — quoting style 차이만] "The client creates and records a secret named the code_verifier and derives a transformed version t(code_verifier) (referred to as the code_challenge)."
|
|
|
|
> [§1.1 Protocol Flow, 2026-05-27 verified verbatim with single-quote markers] "The client creates and records a secret named the 'code_verifier' and derives a transformed version 't(code_verifier)' (referred to as the 'code_challenge')."
|
|
|
|
> [§4.2 Client Creates the Code Challenge, 2026-05-27 verified MATCH] "code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))"
|
|
|
|
> [§1.1 Protocol Flow, 2026-05-27 verified MATCH] "The authorization server transforms code_verifier and compares it to t(code_verifier) from (B). Access is denied if they are not equal."
|
|
|
|
> [§1.1 Protocol Flow, 2026-05-27 verified MATCH] "An attacker who intercepts the authorization code at (B) is unable to redeem it for an access token, as they are not in possession of the code_verifier secret."
|
|
|
|
(2026-05-27 note: 원본 RFC 7636 §1.1 의 "(B)" 단계 인용 출처는 §4.6 이 아닌 §1.1 Protocol Flow 내부. 2026-05-25 capture 가 §4.6 으로 잘못 식별한 것을 본 재검증에서 정정.)
|
|
|
|
## Claims Extracted / 추출된 주장
|
|
|
|
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
|
|---|---|---|---|---|---|
|
|
| PKCE-RFC7636-C1 | OAuth 2.0 public client가 Authorization Code Grant 사용 시 authorization code interception attack에 취약 | [§1 Introduction] "OAuth 2.0 public clients utilizing the Authorization Code Grant are susceptible to the authorization code interception attack." | `official-standard` | OAuth 2.0 public client (SPA, native app 등 client secret을 안전하게 보관 불가한 client) | confidential client (server-side, client secret 보호 가능) 가 동일 공격에 취약하다는 뜻은 아님 — RFC 7636의 직접 scope는 public client |
|
|
| PKCE-RFC7636-C2 | PKCE의 핵심 메커니즘: client가 비밀 `code_verifier`를 생성·기록하고, 변환 함수 `t()`를 적용한 `code_challenge`를 도출 | [§1.1 Protocol Flow] "The client creates and records a secret named the 'code_verifier' and derives a transformed version 't(code_verifier)' (referred to as the 'code_challenge')." | `official-standard` | PKCE를 적용하는 모든 OAuth client | `t()`의 구체적 선택지(plain vs S256)의 보안 동등성을 말하지 않음 — §4.2에서 별도 정의 |
|
|
| PKCE-RFC7636-C3 | S256 method: `code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))` (단방향 SHA-256 해시 + URL-safe Base64) | [§4.2 Client Creates the Code Challenge] "code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))" | `official-standard` | S256 method를 선택한 client (RFC 권장) | plain method도 사용 가능하나 RFC가 S256을 권장하는 정확한 위험 모델 비교는 별도 인용 필요 |
|
|
| PKCE-RFC7636-C4 | Authorization server는 token 교환 시 client가 제출한 `code_verifier`를 동일한 변환 `t()`로 처리하여 (B)단계의 `code_challenge`와 비교, 불일치 시 access 거부 | [§1.1 Protocol Flow] "The authorization server transforms code_verifier and compares it to t(code_verifier) from (B). Access is denied if they are not equal." | `official-standard` | PKCE를 강제하는 Authorization Server의 token endpoint | "거부" 응답의 정확한 error code / HTTP status는 본 인용 범위 밖 (RFC 6749 error mapping에 의존) |
|
|
| PKCE-RFC7636-C5 | 공격자가 (B) 단계에서 authorization code를 가로채도 `code_verifier`가 없으면 access token으로 교환 불가 | [§1.1 Protocol Flow] "An attacker who intercepts the authorization code at (B) is unable to redeem it for an access token, as they are not in possession of the code_verifier secret." | `official-standard` | code interception 공격 모델 (악성 앱이 redirect URI 가로채는 시나리오) | `code_verifier` 자체가 client device 외부로 유출된 경우의 방어는 별도 — PKCE는 transport 단계 가로채기 방어만 |
|
|
|
|
## Usage Boundaries / 적용 경계
|
|
|
|
- **이 자료가 직접 증명하는 것**:
|
|
- `PKCE-RFC7636-C1`~`C5`: PKCE 표준 메커니즘 (code_verifier/challenge 생성·검증, S256 공식, 위협 모델)
|
|
- **이 자료가 증명하지 않는 것**:
|
|
- PKCE가 confidential client에도 의무라는 점 (RFC 7636 범위는 public client. OAuth 2.1 draft / RFC 9700에서 확장 — 본 문서 범위 밖)
|
|
- `code_verifier` 길이 (43~128 char) / 허용 문자 정확한 spec — 본 발췌 인용에 포함 안 됨, §4.1 참조 권고
|
|
- Keycloak이 client별 PKCE 강제 옵션을 어떻게 노출하는지 (Keycloak vendor 문서 참조)
|
|
- Implicit flow의 PKCE 적용 불가 사실 (RFC 8252에서 다룸, 본 RFC 범위 밖)
|
|
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
|
- Keycloak realm/client 설정에서 "Proof Key for Code Exchange Code Challenge Method = S256" 강제 설정 위치
|
|
- vanilla JS SPA에서 `crypto.subtle.digest('SHA-256', ...)` + `base64url` encoding 호환성 (IE/구형 브라우저 미지원, modern only)
|
|
- code_verifier를 sessionStorage에 둘 때 XSS 노출 위험과 single-page lifetime 일치성
|
|
|
|
## P2A/P3A 함의 (내 프로젝트 해석)
|
|
|
|
> 본 섹션은 자료 직접 인용이 아닌 패턴 결정 컨텍스트 해석. wiki 추출 시 `wiki/concepts/` 또는 `wiki/projects/` source-summary로 옮겨야 함.
|
|
|
|
- `code_verifier`: 43~128 byte 길이의 `[A-Z][a-z][0-9]-._~` random string (§4.1).
|
|
- Method:
|
|
- `plain`: `code_challenge = code_verifier` — **권장 안 함**.
|
|
- `S256`: `BASE64URL(SHA256(verifier))` — 표준 선택지. Keycloak도 client 설정에서 강제 가능.
|
|
- 공격 시나리오 (mobile / SPA 공통): redirect URI를 가로채는 악성 앱/스크립트가 code를 캡처 → 정상 client보다 먼저 `/token` 호출. PKCE 없으면 토큰 발급, 있으면 verifier 불일치로 거부.
|
|
- OAuth 2.1 draft는 PKCE를 **모든 client (confidential 포함)**에 의무화 — RFC 7636의 범위를 public client에서 전체로 확장.
|
|
- ID 흐름 (Implicit)은 PKCE 적용 불가 — 그래서 RFC 8252 / OAuth 2.1에서 deprecated.
|
|
|
|
## 메모 / Notes
|
|
|
|
- 2026-05-27 re-verification: WebFetch 재확인 완료. 5개 quote 모두 verbatim MATCH (C2 는 single-quote vs underscore quoting style 차이만 — 의미 동일하므로 official-standard 유지). C4/C5 의 anchor 가 §4.6/§1 가 아닌 §1.1 Protocol Flow 임을 정정.
|
|
- code_verifier 길이/문자셋 규칙 (§4.1) 은 본 raw에 직접 인용으로 보관 안됨 — 후속 raw 또는 wiki/concepts 정리 시 추가 발췌 필요.
|
|
|
|
## Related / 관련
|
|
|
|
- 같은 주제 다른 official-doc:
|
|
- [[raw/official-docs/security-oauth2-pkce-rfc-8252]] — RFC 8252가 native app에서 PKCE를 MUST로 의무화
|
|
- 인용하는 branch:
|
|
- [[raw/branch-notes/feature-keycloak-patterns]]
|
|
- [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]]
|
|
- [[raw/branch-notes/feature-keycloak-single-ec2-no-google]]
|
|
- [[raw/branch-notes/feature-keycloak-vanilla-js-spa-pkce]]
|
|
- [[raw/branch-notes/feature-keycloak-pkce-flow-stages]]
|
|
- 인용한 wiki 요약: (미작성)
|