본문 바로가기

사이드 프로젝트/OAuth2.0을 활용한 토큰 발급 서버 개발 프로젝트

OAuth2.0을 활용한 로그인 및 토큰 발급 서비스 개발 후기 2

안녕하세요.

이번 글에서는 OAuth2.0을 활용하여 로그인 서비스를 구현하며 들었던 의문점, OAuth2.0이 왜 이렇게 설계되었는지에 대한 호기심 등을 자유롭게 분석한 내용을 공유하도록 하겠습니다.

 

 

앞서 1부에서 소개한 OAuth2.0의 흐름은 아래와 같았습니다.

 

OAuth2.0 flow

 

의문점은 주로 3 ~ 5번 과정에 있었습니다. '왜 굳이 Authorization Code를 발급할까? 로그인 -> Authorization Code 발급 요청 -> WAS로 Redirect ->  Access Token 발급 ... 등등 일련의 복잡한 과정들 없이, 로그인에 성공하면 바로 Access Token을 전달하면서 WAS로 리다이렉트하면 안될까?'  등의 여러 의문점이 있었습니다. 또한, 추가적인 궁금증도 많았습니다.

 

이러한 궁금증을 해결하기 위한 첫 단추로 도움이 되었던 글은 'OAuth와 함께 춤을'이라는 제목의 Naver D2 글이었습니다.

https://d2.naver.com/helloworld/24942 

 

위 글은 OAuth1.0에 대한 내용이고 OAuth1.0과 2.0을 비교하며 OAuth2.0 설계에 대한 의문들을 풀 수 있었습니다.

 

 

OAuth1.0 흐름을 단계별로 정리하면 아래와 같습니다.

  • Request Token 발급

클라이언트가 OAuth Provider에 Request Token 발급을 요청합니다. Request Token 발급을 위해 사용되는 주요 파라미터는 아래와 같습니다. 요청에 성공하면 클라이언트는 {oauth_token, oauth_token_secret}을 전달 받습니다.

  - oauth_callback : Request Token 발급 이후, 인증에 성공하면 리다이렉트할 웹 주소를 이 단계에서 지정함

  - oauth_consumer_key : OAuth Provider가 클라이언트를 구별하기 위해 사용하는 값

  - oauth_signature :  파라미터를 대칭키 알고리즘으로 암호화한 값입니다. 클라이언트와 OAuth Provider 사이 위변조가 없었는지를 확인하기 위해 사용함

  • 인증 요청

Request Token 요청에 대한 응답으로 받은 oauth_token을 전달하며 인증을 시도합니다. 인증에 성공하면 앞선 단계에서 저장하고 있던 CallBackURL로 리다이렉트 합니다. 이때 CallBackURL에 쿼리 파라미터로 새로운 oauth_token과 oauth_verifier를 함께 전달합니다.

  • Access Token 발급

Request Token 요청에 대한 응답으로 받은 oauth_token_secret과 인증 요청에 대한 응답으로 받은 새로운 oauth_token을 요청 파라미터로 Access Token 발급 요청을 합니다. Request Token 과정과 마찬가지로 대칭키 암호화를 합니다.

 

 

1. 왜 Authorization Code 또는 Request Token이 필요할까?

  Authorization Code(Request Token) 자체에는 사용자의 민감한 정보가 포함되어 있지 않습니다. Authorization Code에는 Access Token 발급을 위한 기본적인 정보와 짧은 생명주기를 갖기 때문에 악의적인 사용자에게 탈취 당하더라도 어느 정도 문제를 막을 수 있습니다. 더욱이, OAuth1.0과 2.0 모두 인증이 완료된 이후에 CallBackURL을 사용하여 즉시 Access Token을 발급하도록 Redirect할 수 있으므로 짧은 시간 내에 Authorization Code(Request Token)가 탈취 당하는 것을 막을 수 있습니다. 그러므로, Authorization Code(Requst Token)로 Access Token을 발급하는 것이 안전하다고 판단했습니다.

 

2. 왜 OAuth2.0이 OAuth1.0보다 더 많이 활용될까?

 OAuth는 인증이 완료된 사용자에게 리소스 접근 권한을 제공하는 오픈 API입니다. 그러므로, OAuth는 "사용자 인증이 완료되면 리소스 접근 권한을 부여한다."의 조건만 지켜지면 된다고 생각했습니다. 그런 면에서 OAuth1.0과 2.0은 그 조건을 지키고 있으므로 기능상 문제가 없습니다.

 

하지만, OAuth1.0은 몇가지 불편함이 있다고 판단했습니다.

 1. OAuth1.0은 Request Token 발급, 인증, Access Token 발급 3단계를 거칩니다. 인증과 Access Token 발급 과정을 Redirect를 활용해 한번에 할 수 있다고 하더라도 Request Token 발급을 포함하면 클라이언트는 직접 최소 2번 요청을 OAuth Provider에게 보내야 합니다. 

 2.  Request Token을 발급하면, OAuth Provider는 클라이언트가 인증 완료 시 호출 해야할 CallBackURL을 기억해야 하므로 Read-Write 작업에 대한 부담도 있습니다.

 3. 무결성과 기밀성을 지키기 위해서 대칭키에 의해 암호화된 signature를 사용했으나 이를 암복호화하는 것에도 부담이 있었습니다. 

 4. OAuth1.0에서의 Access Token은 유효기간이 무제한이므로 악의적인 사용자에게 탈취당하면 위험합니다.

 

OAuth2.0의 설계가 1번, 2번 해소에 기여했다고 판단했습니다.

OAuth1.0의 (Request Token 발급 -> 인증 -> Access Token 발급) 과정을

OAuth2.0에서는 (인증 -> Authorization Code 발급 -> Access Token 발급) 과정으로 변경하면서 클라이언트는 1번의 요청으로만으로도 Access Token을 발급할 수 있도록 하여 불편함을 개선했습니다. 또한, Authorization Code 및 Access Token 발급을 자동화하기 위해 OAuth Provider가 아닌 중개 서버(WAS)에 CallBackURL을 저장함으로써 OAuth Provider가 CallbackURL을 기억해야 한다는 부담도 줄일 수 있었습니다.

 물론, OAuth Provider의 부담은 감소했지만 URL 저장을 위해 중개서버(WAS)를 반드시 도입해야 한다는 어려움이 생겼습니다. 그러나, 중개 서버(WAS)의 도입은 3번, 4번 문제를 해결에 기여할 수 있다고 판단했습니다. OAuth1.0 에서는 대칭키 암호화를 통해 무결성과 기밀성을 지킬 수 있었으나 구현의 복잡함과 암복호화의 부담이 있었습니다. 이에 OAuth2.0에서는 클라이언트와 OAuth Provider 사이 중개 서버를 도입함으로써 항상 HTTPS 통신하도록 하여 기밀성과 무결성을 지킬 뿐더러 직접 암복호화 해야 하는 부담을 줄였습니다. HTTPS 브라우저에서 HTTP 요청으로 다운 그레이드가 불허되는 현재의 상황까지 이어진 HTTPS의 히스토리 또한 OAuth2.0 확산에 더 힘을 실었다고 생각했습니다.

 또한, 중개서버에서의 적절한 Access & Refresh Token 관리 정책을 통해 클라이언트에게 전달한 Access Token을 만료 및 갱신할 수 있는 메커니즘을 갖출 수 있었습니다. 이처럼, OAuth2.0은 중개서버 도입이라는 새로운 것이 필요했지만, 이를 도입하여 OAuth1.0에 있었던 여러 문제를 개선할 수 있었습니다.

 

 

3. OAuth2.0의 취약점은?

OAuth2.0에서는 302 Redirect 방식으로 Authorization code를 전달하므로 악의적인 사용자가 이를 탈취하여 Access Token을 발급할 수 있습니다. 이를 중간자 공격이라고 합니다. 이에 대응하기 위한 방법으로는 아래와 같습니다.

  • PKCE(Proof Key for Code Exchange) : 클라이언트는 Authorization Code 요청 시 code_challenge를 함께 보내고, code_verifier를 기억합니다. Authorization Code 발급 이후 Redirect 시 중개 서버로 code_challenge와 code_verifier를 함께 보내어 비교하여 Authorization Code가 탈취된 것인지 확인할 수 있습니다.

 

4. 모바일 앱 및 네이티브 환경에서의 OAuth2.0 동작 방식은?

 OAuth2.0의 핵심은 적절한 인증을 통해 Authorization Code를 발급 받고, Authorization Code를 통해 Access Token을 발급하는 것이었습니다.

 모바일 앱 또는 네이티브 환경에서도 유사한 방식으로 동작합니다. 외부 브라우저를 통해 Authorization Code를 발급합니다. 그 후, WAS로 리다이렉트하여 Access Token을 발급하는 것이 아닌 모바일 앱 환경으로 리다이렉트하여 Access Token을 발급합니다. 이때, Authorization Code가 탈취되는 것을 방지하기 위해서 PKCE를 활용하여 네이티브 앱에서 code_challenge와 code_verifier를 관리합니다. 

 

5. Client-Secret이란?

서버 - 서버 사이 인증에 사용됩니다. Client-Secret을 통해 Authorization Code와 같은 인증 과정 필요없이 Access Token을 발급할 수 있습니다. 웹 어플리케이션 같은 경우 Client-Secret을 안전하게 보관할 수 있으므로 사용될 수 있습니다. 그러나, 브라우저나 모바일 앱 등에서는 안전하게 보관될 수 없으므로 사용되면 위험합니다.

 

OAuth2.0과 관련하여 다양한 각도로 공부해볼 수 있다는 점이 매우 흥미로웠던 개인 프로젝트였습니다.

마치겠습니다.