한국어
  • auth

PKCE란 무엇인가: 기본 개념에서 깊은 이해까지

이 기사는 PKCE(Proof Key for Code Exchange)가 OAuth 2.0 인증 코드 플로우를 어떻게 보안하는지, 그리고 악의적인 애플리케이션이 인증 코드를 가로채는 것을 방지하는 방법을 설명합니다. 여러분을 기본 개념에서 포괄적인 이해로 안내합니다.

Yijun
Yijun
Developer

Proof Key for Code Exchange (PKCE)는 Authorization Code flow에 대한 확장 기능으로, 원래 모바일 앱에서 인증 코드 플로우를 보호하기 위해 설계되었습니다. 이제는 단일 페이지 앱에서도 사용하는 것이 권장됩니다. OAuth 2.1부터는 public clientsconfidential (private) clients를 포함한 모든 유형의 클라이언트에 대해 PKCE가 필수입니다.

이 기사에서는 왜 PKCE가 만들어졌고, 어떻게 여러분의 애플리케이션을 보호하는지를 이해하는 데 도움을 줄 것입니다. PKCE에 대한 깊은 이해를 제공합니다.

PKCE가 왜 필요한가?

OAuth 2.0 인증 코드 플로우에서 사용자는 애플리케이션을 통해 로그인 요청을 합니다. 인증 서버는 사용자를 인증 페이지로 안내합니다. 사용자 인증 후, 서버는 애플리케이션에 인증 코드를 반환하고, 애플리케이션은 이 코드를 사용하여 인증 서버에서 액세스 토큰을 요청합니다.

이 플로우에는 중요한 보안 위험이 있습니다: 악의적인 프로그램이 인증 코드를 가로챌 수 있는 것입니다. 특히 모바일 기기에서는 다른 애플리케이션이 동일한 리디렉션 URI를 등록하고 인증 코드를 가로챌 수 있습니다.

다음 다이어그램은 가로채기의 과정을 보여줍니다:

Step (1): 앱은 가로챌 수 없는 안전한 API를 통해 인증 요청을 수행합니다. 이 단계에서 요청자는 리디렉션 URI도 제공합니다.

Step (2): 요청은 OAuth 2.0 인증 서버로 전달됩니다. OAuth는 TLS 사용을 요구하기 때문에, 이 통신은 TLS에 의해 보호되며 가로챌 수 없습니다.

Step (3): 인증 서버는 인증 코드를 반환합니다.

Step (4.a): 인증 코드는 단계 (1)에서 제공된 리디렉션 URI를 통해 요청자에게 반환됩니다. 이 단계에서, 만약 악의적인 앱이 리디렉션 URI의 핸들러로 등록되어 있으면, 악의적인 앱은 인증 코드를 가로챌 수 있습니다. 인증 코드를 갖고 공격자는 단계 (5.a)와 (6.a)에서 액세스 토큰을 요청하고 얻을 수 있습니다.

위에서 보듯이, OAuth 2.0 인증 코드 플로우에서는 인증 코드가 가로채진 경우, 공격자가 그것을 이용하여 액세스 토큰을 얻을 수 있습니다. 따라서 인증 코드 가로채기를 방지하는 메커니즘이 필요하며, 이는 PKCE의 탄생으로 이어졌습니다.

PKCE는 어떻게 작동하는가?

위에서 설명한 것처럼, 공격을 방지하려면 요청을 시작한 앱만이 액세스 토큰을 요청하고 얻을 수 있도록 해야 합니다. 이때 PKCE가 작용합니다.

PKCE는 "보증 키" 개념을 도입하여 이 문제를 해결합니다.

인증 코드를 요청할 때, 애플리케이션은 먼저 랜덤 코드 검증기를 생성하고 로컬에 저장합니다. 그런 다음 이 코드 검증기를 특정 알고리즘을 사용하여 코드 챌린지로 변환합니다. 애플리케이션은 인증 코드 요청 중에 코드 챌린지와 코드 챌린지 방법 둘 다를 인증 서버에 보냅니다.

코드 검증기는 임의로 생성된 문자열이며, 코드 챌린지는 변환을 통해 코드 검증기에서 파생됩니다. 두 가지 변환 방법이 지원됩니다:

  • plain:코드 검증기를 그대로 코드 챌린지로 사용
  • S256:SHA-256 해시를 코드 검증기에 적용한 후 Base64URL 인코딩을 합니다. 해시 출력은 코드 검증기를 얻기 위해 역산할 수 없으며, plain 방법이 전송 중 중간자 공격에 취약할 수 있기 때문에, 보안상의 이유로 S256을 사용하는 것이 강력히 권장됩니다.

사용자 인증 후, 인증 서버는 애플리케이션에 인증 코드를 반환합니다. 액세스 토큰을 요청할 때, 애플리케이션은 인증 코드와 코드 검증기를 인증 서버에 보냅니다. 서버는 이전에 받은 "코드 챌린지 방법"을 사용하여 "코드 검증기"를 변환하고, 결과를 이전에 받은 "코드 챌린지"와 비교하여 클라이언트가 "코드 검증기"를 소유하고 있는지 확인합니다.

Step (1-3): 앱은 "코드 검증기"라는 비밀을 생성하고 기록하며, "코드 챌린지"의 변환 버전을 파생하여 OAuth 2.0 인증 요청에 변환 방법인 "코드 챌린지 방법"과 함께 전송합니다.

Step (3-6): 인증 서버는 평소와 같이 응답하지만 "코드 챌린지"와 "코드 챌린지 방법"을 기록합니다.

Step (7.a): 앱은 인증 코드를 평소대로 토큰 엔드포인트에 전송하지만, (1)단계에서 생성한 "코드 검증기" 비밀을 포함합니다.

Step (8.a-9.a): 인증 서버는 "코드 검증기"를 "코드 챌린지"로 변환하고, (1-3)단계의 "코드 챌린지"와 비교합니다. 같지 않으면 접근이 거부됩니다.

이 경우, 악의적인 앱이 (6.b)단계에서 인증 코드를 가로챘더라도, "코드 검증기" 비밀을 소유하지 않기 때문에 액세스 토큰을 요청할 수 없습니다. "코드 검증기"가 TLS를 통해 전송되기 때문에 가로챌 수 없습니다.

요약

이 기사는 PKCE가 어떻게 작동하는지, 그리고 인증 코드 플로우를 보호하기 위해 왜 필요한지를 설명합니다. 증명 키 메커니즘을 추가함으로써, PKCE는 악의적인 애플리케이션이 인증 코드를 가로채고 악용하는 것을 방지합니다. 이 설명이 PKCE를 깊이 있게 이해하는 데 도움이 되길 바랍니다.