state는 인가 요청과 callback을 현재 사용자 세션에 연결하고 위조된 응답을 걸러내는 값입니다. PKCE는 인가 요청에서 보낸 code_challenge와 token 교환 때 제시하는 code_verifier를 비교해 탈취된 authorization code만으로 Access Token을 받기 어렵게 합니다. OIDC nonce는 인증 요청을 ID Token에 연결해 값이 일치하는지 확인하고 replay 또는 code injection 위험을 줄이는 데 쓰입니다.

세 기능은 검증 위치가 다르지만 위협 방지 범위가 전혀 겹치지 않는 것은 아닙니다. RFC 9700은 안전하게 결속한 state 외에도 PKCE와 OIDC nonce가 일부 CSRF 대응을 제공할 수 있다고 설명합니다. 따라서 단어를 하나씩 외우는 대신 요청에서 생성하고 응답의 어느 필드와 대조하는지 추적해야 합니다.

먼저 값이 오가는 위치를 따라갑니다

OAuth 로그인 요청이 브라우저를 거쳐 Authorization Server로 갔다가 Client로 돌아온다고 합시다. state는 Client가 요청 전에 만든 임의 값이며, 응답에서 되돌아온 state를 현재 로그인 시도에 저장해 둔 값과 비교합니다. RFC 9700은 CSRF 방어로 state를 쓸 때 이 값이 사용자 에이전트 세션에 안전하게 연결되어야 한다고 설명합니다. 서버가 state를 넣었다는 사실만으로 방어가 완성되는 것은 아닙니다. 반환값을 비교하지 않거나 세션에 묶지 않으면 그 연결 확인이 무너집니다.

PKCE는 각 authorization request마다 code_verifier라는 비밀을 만들고, 변환한 code_challenge를 요청에 보냅니다. Client가 authorization code를 받은 뒤 token endpoint에 code_verifier를 제시하면 Authorization Server가 저장한 challenge와 비교합니다. 코드만 가로챈 제3자는 verifier를 모르면 token을 교환하기 어렵습니다. 이 검사는 token endpoint에서 이뤄지며, verifier가 안전하게 만들어지고 저장되는지와 서버가 비교를 실제로 강제하는지도 중요합니다.

OIDC nonce는 요청에 넣은 값이 ID Token의 nonce claim으로 돌아왔는지 Client가 확인해 요청과 인증 결과를 결속하고 replay·code-injection 위험을 줄입니다. RFC 9700은 일부 CSRF 보호도 언급하지만 nonce를 보내는 것만으로 충분하지 않습니다. Client는 ID Token 서명·issuer·audience·시간을 확인하고 nonce까지 검증한 뒤 사용합니다. OpenID Connect Core §3.1.2.7은 code flow 응답 검증, §3.1.3.7은 ID Token 검증을 다룹니다. nonce는 일반 Access Token의 claim이 아닙니다.

직접 만든 가상 authorization-code 흐름의 확인 지점
값요청에서 하는 일응답에서 대조하는 곳놓치면 생기는 문제
stateClient 세션의 현재 로그인 시도와 임의 값 연결callback 응답에 돌아온 state와 저장값 비교다른 세션에서 온 응답을 해당 로그인과 혼동할 수 있음
PKCE code_challengeverifier의 변환값을 Authorization Server에 등록token endpoint에서 받은 verifier를 변환해 challenge와 비교authorization code만 훔친 주체가 token 교환을 시도할 수 있음
OIDC nonce인증 요청과 ID Token에 담길 임의 값 연결ID Token의 nonce claim과 요청에서 저장한 값 비교다른 인증 응답 또는 재사용된 ID Token을 잘못 받아들일 수 있음

한 값이 있다고 나머지 검사를 지우지 않습니다

가상 흐름에서 Client는 state=S1, verifier=V1, nonce=N1을 만들고 challenge를 요청에 보냅니다. callback의 state가 저장값과 맞는지 확인한 뒤 token endpoint는 V1을 challenge와 비교합니다. OIDC Client는 ID Token의 서명·issuer·audience·시간을 확인하고 nonce=N1의 일치도 검사합니다. 세 검사는 흐름의 서로 다른 지점에서 이뤄집니다.

state가 S1과 맞더라도 authorization code가 탈취되지 않았다는 뜻은 아닙니다. PKCE 검사가 verifier 없이도 통과하도록 구현됐다면 코드 재사용 위험이 남습니다. 다만 RFC 9700 §4.7.1은 Authorization Server(AS)가 PKCE를 지원한다는 점을 확실히 확인한 경우 PKCE가 state와 같은 CSRF 보호를 제공할 수 있다고 설명합니다. 따라서 ‘PKCE는 state의 CSRF 보호를 대신할 수 없다’고 단정하지 않습니다. 반면 애플리케이션이 state에 복귀 경로나 요청별 문맥을 담는다면 그 값의 무결성·세션 결속 검증은 별도 애플리케이션 요구사항입니다. CSRF 방어의 겹침이 그 문맥을 신뢰해도 된다는 뜻은 아닙니다.

요청값을 만들고 검증하는 주체를 순서대로 적습니다

  1. 로그인 전에 Client가 생성해 세션과 연결한 state·code_verifier·nonce를 식별합니다.
  2. Authorization Request에 실제로 보낸 값과 Authorization Server가 기록할 값을 구분합니다.
  3. callback에서 state를 현재 요청의 값과 비교하고 응답이 해당 브라우저 세션에 속하는지 확인합니다.
  4. token endpoint에서 code_verifier가 원래 code_challenge를 만족하는지 확인합니다.
  5. OIDC라면 ID Token의 nonce를 요청값과 비교하고 signature, issuer, audience, 시간 검증을 별도로 완료합니다.
  6. 검증 실패 시 token을 사용하지 않고, 같은 공격을 여러 값이 부분적으로 막을 수 있다는 전제를 답안에 남깁니다.

state는 authorization response 값과, PKCE는 token 요청 verifier와, nonce는 ID Token claim과 대조합니다. RFC 9700 §4.7.1은 AS의 PKCE 지원을 확실히 확인하면 PKCE가 CSRF 보호를 제공할 수 있다고 하고, §4.5.3은 state의 user-agent session 결속을 다룹니다. state에 실린 복귀 경로나 업무 문맥의 무결성은 별도이며, 필요한 OIDC ID Token 검증도 생략하지 않습니다.

세 문자열을 서로 교환 가능한 비밀번호로 여기지 않습니다

흔한 오답은 세 값을 모두 ‘CSRF 토큰’이라고 부르거나 nonce와 state를 하나로 합치면 모든 OIDC 응답이 안전해진다고 쓰는 것입니다. state는 callback을 현재 요청에 묶고, PKCE는 코드 교환 권한을 verifier에 결속하며, nonce는 인증 결과 ID Token을 요청 세션에 묶습니다. 실제 설계에서는 두 값이 일부 위협을 함께 줄일 수도 있지만 프로토콜 메시지와 검사 위치가 다릅니다. Client가 무엇을 검증해야 하는지 flow 문서로 확인해야 합니다.

응답 필드마다 대조할 요청값을 말해 봅니다

회상 연습: callback은 code=C2, state=S1을 반환했고 현재 브라우저가 저장한 state는 S2입니다. Client는 code를 먼저 교환하지 말고 state 불일치를 거부해야 합니다. 다른 시도에서 state는 일치하지만 token endpoint에 보낸 code_verifier가 저장된 code_challenge와 다르면 Authorization Server가 token을 내주지 않아야 합니다. OIDC ID Token의 nonce가 저장된 nonce와 다르면 Client는 해당 인증 응답을 받아들이지 않습니다. RFC 9700이 일부 방어의 겹침을 인정해도 검증 위치는 각각 기록해야 합니다. 이 trace는 직접 만든 교육 예이며 실제 기출이나 집중 180제 카드가 아닙니다. 상품 구성은 /decks/security#focus-180에서 확인하세요.

정답을 가리고 확인해 보세요

이 글을 위해 만든 학습 예시이며 공식 기출문제나 판매 카드의 인용이 아닙니다.

AS가 PKCE를 지원한다는 점을 확인한 로그인에서 callback state가 저장값과 다르고, 앱은 state에 복귀 경로도 연결해 두었다. 또 다른 시도에서는 token endpoint의 code_verifier가 challenge와 맞지 않는다. CSRF 방어 조건과 앱 문맥의 무결성을 나눠 설명하고, OIDC nonce는 어디서 확인할까?

정답과 해설 확인AS의 PKCE 지원을 확실히 확인했다면 PKCE가 CSRF 보호를 제공할 수 있어 state가 반드시 그 방어에만 쓰이는 것은 아니다. 하지만 state가 저장된 복귀 경로와 맞지 않으면 앱은 그 경로를 신뢰하지 말아야 한다. verifier 불일치는 token 교환을 거부하게 하고, OIDC nonce는 ID Token claim을 요청값과 비교한다.

RFC 9700 §4.7.1의 PKCE 기반 CSRF 보호에는 AS의 지원을 확실히 아는 조건이 있다. 이 조건은 state에 저장한 애플리케이션 문맥의 무결성 검증을 대신하지 않는다. PKCE verifier 비교와 OIDC nonce 검증도 각 프로토콜 위치에서 유지한다.

이 주제의 회상 연습 이어가기