LabHub

Keycloak 과 기업 인증 · 애플리케이션 연동(리다이렉트·PKCE) · 이론

PKCE 흐름을 한 단계씩

LabHub 에서 이어서 보기

한 줄 요약

PKCE 는 인가 코드에 "이 코드는 내가 시작한 요청의 것"이라는 증명을 붙이는 장치다. 시크릿 없이도 코드 가로채기가 무력해진다.

왜 이게 필요했나

모바일 앱에서 커스텀 URL 스킴으로 콜백을 받던 시절, 악성 앱이 같은 스킴을 등록해 인가 코드를 가로채는 공격이 실제로 가능했습니다. 코드만 있으면 토큰을 받을 수 있었기 때문입니다(공개 클라이언트라 시크릿이 없으므로).

PKCE 는 이 문제를 해결합니다. 코드를 가로채도 토큰을 못 받게 만듭니다.

어떻게 동작하나

흐름을 순서대로 보면 이렇습니다.

먼저 클라이언트가 무작위 문자열 code_verifier 를 만듭니다. 43자 이상 128자 이하, URL 안전 문자만 씁니다. 이 값은 클라이언트만 알고 있습니다.

다음으로 code_challenge = BASE64URL(SHA256(code_verifier)) 를 계산합니다. 인가 요청 URL 에 code_challengecode_challenge_method=S256 을 실어 보냅니다. plain 방식도 규격에는 있지만 해시를 하지 않는 것이라 의미가 없습니다.

사용자가 인증 서버에서 로그인하면 redirect_uri 로 인가 코드가 돌아옵니다. 이때 state 도 함께 돌아오는데, 클라이언트가 처음 보낸 값과 같은지 반드시 확인해야 합니다. 다르면 자기가 시작한 요청이 아닙니다.

마지막으로 토큰 엔드포인트에 인가 코드와 함께 원본 code_verifier 를 보냅니다. 서버가 해시를 다시 계산해 저장해 둔 code_challenge 와 비교합니다. 맞으면 토큰을 줍니다.

공격자가 코드를 가로챘다고 해도 code_verifier 를 모르므로 교환에 실패합니다.

현장에서 만나는 모습

리다이렉트 URI 등록에서 자주 사고가 납니다. http://localhost:*https://example.com/* 같은 넓은 와일드카드를 등록하면, 그 도메인에 열린 리다이렉터가 하나만 있어도 토큰이 새어 나갑니다. 정확한 경로를 등록하는 것이 원칙입니다.

리프레시 토큰 회전도 실무의 기본입니다. 갱신할 때마다 새 리프레시 토큰을 주고 옛것을 무효화합니다. 옛 리프레시 토큰이 다시 쓰이면 탈취를 의심해 그 세션 전체를 끊습니다. 저자의 권고 조합은 짧은 액세스 토큰(5~15분)과 리프레시 토큰 회전입니다 — 블랙리스트 없이도 실질적 무효화 효과를 얻습니다.

그리고 BFF 패턴을 고려할 만합니다. 토큰을 브라우저에 아예 주지 않고 백엔드가 보관하며 세션 쿠키로만 통신하는 방식입니다. XSS 로 토큰이 털리는 경로 자체가 사라집니다.

다음 실습에서 할 것

PKCE 흐름 전체를 curl 과 스크립트로 완주합니다. verifier 와 challenge 를 만들고, 인가 요청을 보내고, 로그인 폼을 POST 해서 코드를 받고, 토큰으로 교환하고, 틀린 verifier 로는 실패하는 것까지 확인합니다. 그다음 별도 실습에서 그 토큰을 검증하는 미들웨어를 FastAPI 앱에 붙입니다.