Oauth에 PKCE 적용하기

     

회사에서 어쩌다 보니 3rd party와 연동하는 프로젝트를 진행하게 되었습니다. 기존에 open-api로 제공되던 oauth 방식의 로그인 기능을 살짝 고쳐서 3rd party에서 앱 투 앱 로그인을 통해 자사의 데이터를 조회할 수 있는 API를 제공하는 프로젝트입니다. 

 

살짝 마이데이터랑 비슷한데, 우리 회사는 아직 마이데이터 사업자에 포함되어있지 않아서, 독자적으로 데이터를 제공하기 위해 Oauth연동을 붙이고 데이터를 조회해 갈 수 있게끔 만드는 작업입니다.

 

기존에 개인 사용자들이 open-api를 조회하기 위해 oauth 연동 과정, /authorize를 통해 인가 코드(Authorization_code)를 발급받고, 발급받은 인가 코드를 사용해 토큰(Access_token, Refresh_token)을 발급받는 구조입니다. 그 후 서비스 API 조회를 위해서는 토큰을 사용하면 됩니다.

 

But, 보안팀에서 무엇인가 부족하다고 합니다. PKCE를 적용해야 할 것 같다고 하네요. 

 

What is PKCE?

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

 

RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients

 

datatracker.ietf.org

PKCE(Proof Key for Code Exchange)를 잘 알기 위해서는 역시 공식문서를 보는 게 최고지만, 간단히 설명하면 인가 코드를 발급하는 과정과 토큰을 발급하는 과정 사이에 공격자에 의한 탈취를 막는 방법입니다.

 

인가코드 탈취 후 토큰 발급

 

위 그림은 일반적인 Oauth 2.0의 Flow에서 Malcious App을 이용해 인가 코드를 탈취하고 토큰을 발급하는 상황입니다. 이 과정에서 PKCE는 code_verifier와 code_challenge라는 값을 이용해 공격에 대한 방어를 하게 됩니다. 

 

code_verifier는 임의의 난수입니다. 뭐 어느 정도 규칙은 있긴 한데 공식문서에 따르면 아래와 같습니다.

   code-verifier = 43*128unreserved
   unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
   ALPHA = %x41-5A / %x61-7A
   DIGIT = %x30-39

대충 알파벳이랑 숫자랑 - . _ ~ 요런 것만 사용하라는 뜻입니다.

 

code_challenge는 code_verifier를 해쉬 함수를 통해 해싱하고 BASE64로 인코딩한 값입니다. 해쉬 함수는 아무거나 하면 되는데 만약 SHA256으로 했다면 code_challenge = BASE64( SHA256( code_verifier ) ) 요롷게 됩니다.

 

이 두 가지 값을 언제 쓰냐면,

 

기존에 /authorize를 호출할 때 (인가 코드를 발급할 때) code_challenge를 input param에 넣습니다.

 

      desc 'oauth 권한 위임 동의' do
        success Entities::AuthorizationGrant
      end
      params do
        ...
        optional :code_challenge, type: String, desc: 'challenge code (PKCE)'
      end

 

백엔드에서는 이렇게 code_challenge가 올라오면 저장해놨다가 나중에 토큰을 발급할 때 올라오는 code_verifier를 저장된 code_challenge와 비교하면 됩니다.

 

      desc 'authorization_code 를 통해 oauth access token 발급' do
        success Entities::AccessToken
      end
      params do
        ...
        optional :code_verifier, type: String
      end
      
      
      
      def validate_code!
        raise ::V1::Exceptions::InvalidCodeVerifier unless grant.code_challenge == Base64.urlsafe_encode64(Digest::SHA256.hexdigest(params[:code_verifier]), padding: false)
      end

 

공식문서에는 code_verifier와 함께 사용한 해쉬 함수를 같이 올리라고 되어있긴 한데, 요건 내부적으로 정하면 될 것 같네요.

 

 

 

추가적인 보안 - 앱

PKCE와 별개로 앱 투 앱 로그인 시 연동되는 앱이 정상적인 앱인지 인증하는 과정이 필요한데, 요즘에 하도 피싱앱이 많아서 그런 것 같습니다. 근데 일단 아이폰은 생태계 자체가 피싱앱을 만들지 못하는 구조이기 때문에 걱정이 없습니다. 문제는 안드로이드인데 앱을 만들거나 스토어에 올리는 게 쉬워서 그만큼 피싱앱도 많습니다.

 

이러한 문제를 해결하기 위해 안드로이드 key hash라는 값을 사용하는데요, 안드로이드 앱에 key store값을 해싱한 값입니다. 요 값은 앱마다 유니크한데, 이 값을 이용해서 실제로 정상적인 안드로이드 앱인지 검증하는 방법입니다.

 

대신 이 key hash값을 이용해 검증하기 위해서는 사전에 3rd party와 서비스 제공자들 간에 key hash값을 공유할 필요가 있습니다. 이것마저 털린다면.. 뭐 할 수 없죠 ㅎㅎ

 

퍼블릭한 서비스에서는 서비스 제공자가 앱 개발자에게 key hash를 등록할 수 있는 화면을 제공함으로써 서비스 이용자가 key hash를 등록할 수 있습니다. 아마 여러분이 카카오나 네이버에 api를 사용하기 위해서 앱 key hash를 등록하는 과정을 경험해보셨을 수도 있습니다.

 

 

 

간편 로그인이 널리 사용되면서 이제 id와 pw를 치고 들어가는 로그인이 너무 복잡한 UX가 되었습니다. 따라서 많은 플랫폼 사업자 혹은 서비스 들이 Oauth 2.0/2.1을 이용해 간편 로그인 기능을 제공하고 있는데요, 여러분이 Oauth 사업자가 되어 개발을 하게 된다면 PKCE가 적용되어있는지 확인해보세요~ 추가적으로 Oauth에 대한 절차나 보안에 대해서 더욱 잘 이해하실 수 있게 됩니다 ㅎㅁㅎ

 

반응형

댓글

Designed by JB FACTORY