본문 바로가기
배경지식

Proof Key for Code Exchange by OAuth Public Clients

by kakao-TAM 2023. 4. 26.

https://datatracker.ietf.org/doc/html/rfc7636

 

RFC ft-ietf-oauth-spop: Proof Key for Code Exchange by OAuth Public Clients

OAuth 2.0 public clients utilizing the Authorization Code Grant are susceptible to the authorization code interception attack. This specification describes the attack as well as a technique to mitigate against the threat through the use of Proof Key for Co

datatracker.ietf.org

PKCE(Proof Key for Code Exchange)는 OAuth2 Flow에서 인가(Authorization) 과정에 인증코드 가로채기 공격(authorization code interception attack)을 방어하기 위한 방법입니다. 패스워드 변경, 회원가입 등 API호출간 동선의 적합성을 검증하기 위한 수단으로도 많이 활용됩니다. 

 

[개요]

인가코드 발급에서 code_challenge를 포함하고, 인가코드를 이용한 액세스 토큰 발급에 code_verifier함께 API로 보내 검증합니다.

 

[절차 예시]

(1) 클라이언트에서 code_verifier 생성

- 시스템 GUID를 생성 후, UTF8 Byte시퀀스로 인코딩합니다. Byte Array를 SHA512으로 해싱하고 Url 인코딩합니다. 

public static string CodeVerifier()
{
    var HashTool = SHA512.Create();
    Byte[] PhraseAsByte = System.Text.Encoding.UTF8.GetBytes(string.Concat(Guid.NewGuid()));
    Byte[] EncryptedBytes = HashTool.ComputeHash(PhraseAsByte);
    HashTool.Clear();
    return Base64UrlEncoder.Encode(EncryptedBytes);
}

(2) 클라이언트에서 code_challenge 생성

- code_verifier를 UTF8 Byte시퀀스로 인코딩합니다. Byte Array를 SHA256으로 해싱하고 Url 인코딩합니다.

string codeChallenge(string codeVerifier)
{
    if (codeVerifier != null)
    {
        var HashTool = SHA256.Create();
        Byte[] PhraseAsByte = System.Text.Encoding.UTF8.GetBytes(string.Concat(codeVerifier));
        Byte[] EncryptedBytes = HashTool.ComputeHash(PhraseAsByte);
        HashTool.Clear();
        return Base64UrlEncoder.Encode(EncryptedBytes);
    }
    return "";
}

(3) 인가코드 발급 요청 시, code_challenge를 함께 전달합니다.

- state 파람에 난수를 추가 전달하고 RedirectURI로 전달받은 state가 일치하는지 검증하여 인가코드 발급만 따로 검증도 가능합니다.

 

(4) 액세스 토큰 발급 요청 시, code_verifier를 전달합니다. 

- client_secret 파람을 추가 설정하여 사전에 정의된 코드를 알고 있는 클라이언트만 API호출 가능하도록 보안을 강화 할 수 있습니다.

- 액세스 토큰 발급자는 code_verifier를 이용하여 (2)의 절차와 동일하게 code_challenge를 만들고 앞선 (1)에서 받은 code_challenge와 동일한지 검증합니다. 

 

PKCE(code_verifier, code_challenge)와 state, client_secret을 이용하면 인가 처리 시, 외부의 가로채기를 대부분 막아낼 수 있습니다. PKCE는 IETF(Internet Engineering Task Force)에서 만들고 IETF 커뮤니티의 합의를 통해 공개 검토 받고 배포 승인을 받았습니다. 


이러한 PKCE의 안전성으로 이와 유사하게 API 호출 검증하는 경우가 많습니다. 

패스워드 변경과 같은 중요 API 호출 PKCE개념과 세션 일치 여부를 판단하여 API호출 승인하는 일반적인 예시를 공유합니다.

 

(1) code_challenge 전달 API를 호출합니다. 

- API호출 후, 인가코드를 전달 받습니다. 

- 추가로 클라이언트 키해시 또는 번들ID 또는 클라이언트 레퍼러, IP 등. 을 검증해서 호출자를 판단합니다. 

(2) 검증대상 API (ex. 패스워드변경) 를 호출합니다. 

- code_verifier를 이용하여 (2)의 절차와 동일하게 code_challenge를 생성하고 동일한지 비교하고 인가코드를 검증합니다.  

- 인가코드에 짧은 timeout을 설정해도 좋고 앞선 code_challenge 전달 API와 동일한 호출자 정보 검증을 할 수도 있습니다. 

- 모든 검증이 통과되면 중요 로직을 처리합니다.

 

 

댓글