OAuth 2.0 인증 과정에서 PKCE는 ‘인가 코드 가로채기 공격(Authorization Code Interception Attack)’을 방어하기 위해 설계된 핵심 보안 메커니즘입니다. 이 메커니즘은 클라이언트가 서버에 비밀 정보를 저장하지 않아도, 인증 요청의 진위 여부를 암호학적으로 검증하여 공격을 원천 차단합니다. 즉, 공격자가 중간에서 인가 코드를 탈취하더라도, PKCE가 요구하는 고유한 ‘비밀 증명자(Verifier)’ 없이는 액세스 토큰을 획득할 수 없습니다.
OAuth 2.0 인증 과정의 보안 취약점 이해하기
OAuth 2.0은 애플리케이션 간의 권한 위임(Delegation)을 안전하게 처리하는 표준 프로토콜입니다. 그러나 이 표준을 가장 많이 사용하는 단일 페이지 애플리케이션(SPA)이나 모바일 앱 환경에서는 근본적인 보안 취약점이 존재합니다.
전통적인 OAuth 2.0 플로우는 클라이언트가 자신의 신원을 증명하기 위해 client_secret을 사용하도록 설계되었습니다. 문제는 React나 Vue 같은 SPA 환경이나 모바일 앱 환경에서는 이 client_secret을 안전하게 보관할 서버 측 환경(백엔드)이 부재하다는 점입니다.
이러한 환경적 제약은 공격자에게 기회를 제공합니다. 공격자는 이 취약점을 이용해 사용자를 속여 정상적인 로그인 과정을 거치게 만듭니다. 이후 공격자는 인가 서버가 발행한 인가 코드(Authorization Code)를 중간에서 가로채는 것이 가능합니다.
만약 시스템이 PKCE를 적용하지 않았다면, 공격자는 이 가로챈 코드만 가지고 토큰 엔드포인트에 직접 요청을 보내 액세스 토큰을 획득하려 시도할 수 있습니다. 이는 시스템 전체의 보안을 심각하게 위협하는 주요 공격 벡터로 지목되었습니다.
PKCE란 무엇인가요? OAuth 인증 과정의 핵심 원리
PKCE란 무엇인가요? PKCE는 “Proof Key for Code Exchange”의 약자입니다. 이는 OAuth 2.0 플로우를 사용하는 클라이언트가 자신이 가진 비밀 증명자(Proof Key)를 통해 인증 요청의 진위 여부를 암호학적으로 증명하는 방법입니다.
PKCE는 기존의 client_secret에만 의존하던 인증 방식을 ‘일회성, 비대칭적 증명’ 방식으로 업그레이드한 것입니다.
이 메커니즘의 핵심 원리는 간단합니다. 마치 ‘번호표’만으로는 음식을 받을 수 없고, 추가적인 ‘영수증’이나 ‘보증서’가 반드시 필요하도록 시스템을 설계하는 것과 같습니다. 공격자가 아무리 중요한 코드(번호표)를 훔쳐도, 두 번째 단계에서 요구하는 비밀 증명 정보가 없으면 인증을 완료할 수 없습니다.
이러한 증명 과정은 클라이언트가 비밀 키를 안전하게 저장하기 어려운 SPA나 모바일 환경에서 필수적인 보안 장치로 자리매김했습니다.
OAuth 2.0 흐름에서 PKCE가 작동하는 3단계 원리
PKCE는 OAuth 2.0의 권한 부여 코드 흐름(Authorization Code Flow)에 추가되는 보안 확장 기능입니다. 이 과정은 세 단계에 걸쳐 보안을 강화하며, 각 단계마다 특정 암호학적 검증이 이루어집니다.
| 단계 | 과정 설명 | 보안 강화 포인트 |
|---|---|---|
| 1. 코드 생성 (Challenge 생성) | 클라이언트가 임의의 비밀 값 (code_verifier)을 생성합니다. 이 값을 SHA256과 같은 해시 함수를 이용해 고정된 값 (code_challenge)으로 변환하여 인증 요청 시 서버에 함께 전송합니다. |
공격자가 임의의 코드를 예측하거나 가로채도, 이 code_challenge가 없으면 다음 단계로 진행할 수 없습니다. |
| 2. 권한 코드 획득 | 사용자가 인증 절차를 마치면, 클라이언트는 서버로부터 임시 권한 코드(code)를 받습니다. |
이 단계에서는 단순히 코드가 전달될 뿐, 추가적인 암호화 과정은 없습니다. |
| 3. 최종 토큰 교환 | 클라이언트는 획득한 코드와 함께 처음에 사용했던 최초의 암호화된 값(Code Verifier)을 서버에 전송하며 토큰을 요청합니다. | 서버는 전송된 값과 저장된 최초 값을 비교하여 일치 여부를 확인한 후 토큰을 발급합니다. |