ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OAuth 2.0 개념정리 (1)
    카테고리 없음 2024. 12. 22. 16:27

    용어 정리


    우선 OAuth 에서 자주 등장하는 용어에 대해서 간단하게 정리를 해본다.

     

    Confidential Client

    client_secret 값을 기밀로 유지할 수 있는 클라이언트를 말한다. 다른 사람들이 소스코드에 접근할 수 없는 서버사이드의 애플리케이션을 지칭하는 경우가 대부분이다. private github 과 같은 소스코드 저장소를 사용하면 외부에 코드 자체를 노출할 일도 없고 실제 애플리케이션의 산출물 또한 클라우드 환경이나 온프레미스 머신에서 구동되기 때문에 외부 사용자가 접근할 일은 더욱 더 적다.

     

    Public Client

    앞서 본 Confidential Client 와는 반대로 client_secret 값을 기밀로 유지할 수 없는 클라이언트를 말한다.

    모바일 애플리케이션이나 Javascript 앱이 여기에 속한다. 마찬가지로 private github 에 저장하면 소스코드 형상 자체에는 접근하기 여럽지만 모바일 앱의 경우 산출물로 나온 바이너리를 디컴파일하면 난독화가 되어 있더라도 String 값을 어느정도 식별할 수 있고, Javascript 앱의 경우 크롬 개발자 도구를 사용하는 것과 같이 현재 실행중인 앱의 소스코드를 쉽게 확인할 수 있다.

    이런 클라이언트 앱의 경우에는 client_secret 을 사용하지 않는 것이 보안상 안전하다.

     

    Authorization code

    서버사이드 애플리케이션을 사용한 OAuth Flow 에서 사용되는 중간 토큰을 말한다.

    인증 과정 이후에 클라이언트에게 반환되고 클라이언트는 이를 사용해서 엑세스 토큰을 발급받아서 사용한다.

     

    OAuth 2.0에는 네 가지의 서로 다른 역할이 존재한다.

     

    Resource owner

    OAuth 2.0 에서는 사용자를 "resource owner" 라고 칭한다. 왜냐면 사용자의 정보가 리소스가 되기 때문이라고 생각된다.

    사용자는 자신의 계정에 대해 일부 권한을 애플리케이션에 주려고 시도한다. 사용자를 대신해서 이런 리소스(사용자의 정보나 권한)에 접근하고자 하는 시스템은 해당 리소스에 접근하기 전에 반드시 먼저 해당 리소스에 대한 권한을 얻어야 한다.

     

    Resource server

    사용자(resource owner)에 대한 정보를 담고 있는 서버를 말한다. 어떤 시스템이 특정 사용자에 대한 정보를 얻고자 한다면 사용자에게 접근하는 것이 아닌 resource server 에 요청하여 얻어와야 한다. 서버는 전달받은 엑세스 토큰을 검증하여 사용자가 리소스에 대한 접근을 허용했는지를 확인한 뒤 허용되었다면 요청한 정보를 반환해준다.

     

    Authorization Server

    애플리케이션이 사용자의 계정정보에 대한 접근을 요청할 때 대응하는 서버를 말한다. OAuth prompt 를 띄워주는 서버가 바로 이 서버가 된다. 사용자가 권한을 승인하면 그에 맞는 엑세스 토큰을 발급해주는 역할을 담당한다.  Resource server 랑 통합해서 하나의 서버로 운영하는 경우도 있다.

     

    Client

    사용자를 대신해 리소스에 접근하고자 하는 애플리케이션을 말한다. 모바일/웹 애플리케이션이 될 수도 있고 서버사이드 애플리케이션이 될 수도 있다. 사용자를 authorization server로 이동시키거나 애플리케이션이 직접 서버에 접근해서 권한을 받아낸다.

     

    각각의 Role 이 전체적인 OAuth 흐름에서 어떤 부분을 담당하는지는 다음 그림을 참고해보면 좋다.

     

     

    OAuth 2.0 흐름 분석


    OAuth 프로젝트 생성

    OAuth 를 연동한다고 했을 때 가장 먼저 해야하는 일은 우선 우리가 사용할 애플리케이션을 생성해서 등록하는 것이다.

    예를 들어 Google OAuth 를 연동할 때는 Google API Console 에 들어가서 프로젝트를 생성해야 한다.

     

    이 때 애플리케이션의 이름, 로고, 웹사이트 등 정보를 입력하고 프로젝트를 생성하는데 생성이 완료되면 client_id 를 발급받게 된다. 경우에 따라서는 client_secret 을 함께 주는 경우가 있다.

    프로젝트를 생성할 때 가장 중요한 부분은 하나 이상의 redirect URL 을 등록하는 부분이다. OAuth 서비스가 애플리케이션에 대한 인증을 완료한 뒤에 사용자를 어디로 리다이렉션 시켜줄 것인가를 미리 등록하는 것이다.

    공격자가 redirection attack 을 시도하는 것을 막기 위해 미리 등록된 URL로만 이동하도록 만드는 것이다.
    그리고 인증 과정에서 authorization code 가 탈취되는 것을 방지하기 위해서 이러한 redirect URL은 https 를 사용하는 엔드포인트만 사용이 가능하다.

     

    애플리케이션 중에는 OAuth 흐름을 다양한 경로에서 시작해야 하는 경우도 있다. 예를 들어 모바일 애플리케이션과 웹 애플리케이션 양쪽에서 모두 시도해야 하는 경우가 있을 수 있다. 이 경우에는 redirect URL 을 각각에 맞게 여러개를 등록할 수도 있지만, 이 때에는 state 파라미터를 활용하는 것이 더 적합하다.

    state 파라미터는 애플리케이션의 "상태" 를 인코딩해서 전달하는 역할을 담당한다.
    초기 인증 요청에 state 파라미터가 함께 전달되면 인증이 완료된 후에 사용자에게 반환될 때 이 값이 그대로 클라이언트에게 전달된다. 클라이언트는 이 값을 사용해서 인증 이후의 흐름에 활용하면 된다.

    서버 사이드 클라이언트 연동

    백엔드 개발을 하는 경우에는 서버사이드의 클라이언트를 사용해서 OAuth 를 연동하게 된다.

    앞서 용어정리에서 보았듯이 서버사이드 애플리케이션은 client_secret 를 기밀로 보관할 수 있는 confidential client 로 분류된다. 따라서 client_secret 을 활용하는 다음과 같은 흐름으로 진행되게 된다.

    위 그림에서는 example-app.com 이 우리가 구현하고자 하는 서버사이드 클라이언트를 의미한다.

     

    그림을 통해 알 수 있듯이 사용자는 resource server 에 직접 접근하지 않고 우리가 구현한 클라이언트를 통해서만 접근하게 된다. 즉 클라리언트에 자신의 계정 정보에 대한 접근 권한을 부여해서 자신을 대신해서 사용할 수 있도록 한다.

     

    서버 사이드 클라이언트는 authorization code 라는 grant type 을 사용한다.

    authorization code 란 클라이언트가 엑세스 토큰으로 교환할 수 있는 중간 단계의 임시 코드를 말한다.

     

    위의 과정을 순서대로 살펴보면 다음과 같다

     

    1. 사용자가 인증 요청에 대한 승인 혹은 거절을 수행할 수 있도록 한다.

     

    보통 로그인 버튼에 링크를 만들어서 사용자가 이를 클릭하면 인증을 수행할 수 있는 페이지로 이동할 수 있는 링크의 형태로 만든다. 가장 많이 사용하는 형식의 링크는 다음과 같은 형태를 띈다.

    https://authorization-server.com/oauth/authorize
    ?client_id=a17c21ed
    &response_type=code
    &state=5ca75bd30
    &redirect_uri=https%3A%2F%2Fexample-app.com%2Fauth
    &scope=photos

     

    각각의 파라미터는 다음과 같은 의미를 담는다.

    • response_type = code
      • 응답으로 authorization code 를 받겠다는 의미이다. 서버사이드 클라이언트가 활용할 수 있는 방식을 말한다.
    • client_id = a17c21ed
      • OAuth 프로젝트를 등록하면 받는 애플리케이션의 식별자이다.
    • redirect_url = https%3A%2F%2Fexample-app.com%2Fauth
      • 프로젝트 생성 시 등록했던 redirect URL 을 전달한다. 등록했던 URL과 동일한 값을 넣어야 한다. 이 값을 넣는 것은 선택사항이지만 넣는 것이 권장된다고 한다.
    • scope = photos
      • space-separated 형태로 하나 이상의 scope 를 전달한다. 이는 어디까지의 접근을 허용할지를 설정하는데 사용한다.
    • state = 5ca75bd30
      • 리다이렉트 될 때 전달받을 값으로써 사용하거나 인증 후에 수행해야 할 액션에 필요한 정보를 넣어주는데 사용한다. 요청마다 랜덤한 값을 넣어 전달한다면 CSRF protection 의 목적으로도 사용할 수 있다.

    2. 사용자가 인증 페이지에 도달하면 해당 요청에 대한 설명을 해준다.

    어떤 애플리케이션이 사용자의 어떠한 정보에 대한 접근 권한을 요청했는가에 대한 설명을 해주고 이를 승인 할 것인지 거절 할 것인지를 선택하도록 해준다.

    만약 사용자가 승인하면 이미 등록해두었던 redirect URL 로 사용자를 리다이렉션 시켜준다. 이 때 authorization_code 를 포함해서 전달해주고 만약 존재한다면 요청할 때 함께 전달했던 state 파라미터의 값도 포함해서 전달해준다.

    만약 요청을 거절한다면 어떻게 될까?
    이 경우에도 사용자를 등록된 redirect URL 로 리다이렉트 해주지만 authorization code 를 포함하는 대신 query-string 으로 error=access_denied 를 포함한 채로 이동시킨다.
    애플리케이션은 이를 보고 사용자가 요청을 거절했음을 인지하게 되고 이에 대한 알맞은 처리를 수행하면 된다.

     

    3. authorization code 를 access token 으로 교환

     

    애플리케이션은 token endpoint 에 POST 요청을 보내서 access token 을 생성해 사용한다.

    이 요청에는 다음과 같은 파라미터를 포함하게 된다.

    • grant_type
      • 여기서는 authorization code 를 사용해 인증했음을 알려주기 위해 이를 authorization_code 로 설정한다
    • code
      • 인증 완료 후 authorization server 에게서 받은 authorization code 의 값으로 설정한다
    • redirect_uri
      • 최초 인증 요청에 설정했던 redirect url 과 같은 값으로 설정해주면 된다.

    토큰 생성 요청시에는 보통 Client Authenticate 가 필요하다. 보통은 HTTP Basic Auth 를 사용하는 경우가 많긴 하지만 client_id 와 client_secret 을 POST body 에 포함하는 것으로 대체하는 경우도 있다. 이는 자신이 사용하고자 하는 플랫폼의 OAuth spec 을 확인해서 그에 맞게 구현해주면 된다.

     

    4. access token 을 사용해 리소스 획득

     

    3번 과정을 통해 얻은 access token 을 사용해서 resource server 에 요청을 보내면 이제 획득한 권한에 대응되는 리소스를 획득할 수 있게 된다. 이를 사용해서 클라이언트의 로직을 구현해주면 된다.

    엑세스 토큰

    만약 직접 authorization server 를 구현하는 경우거나 JWT 를 사용해서 애플리케이션만의 토큰을 생성하고자 하는 경우라면 엑세스토큰의 유효기간을 설정해야한다. 이 때 유효기간을 얼마나 길게 설정하느냐는 정답이 있는 문제라기보단 각자가 처한 환경에 맞추어 설정하는 것이 좋다. 크게 보면 다음 두 가지 형태로 구현해볼 수 있다.

     

    short-lived access token + long-lived refresh token

    흔하게 사용되는 방식으로 엑세스 토큰을 사용하는 방식에서는 가장 보안이 좋으면서 유연한 방법이라고도 볼 수 있다.

    보통 엑세스 토큰을 직접적으로 만료시킬 수 있는 방법이 없기 때문에 (별도로 DB에 저장하고 있지 않는 이상) 유효기간을 최대한 짧게 설정하고 만료가되면 refresh 를 자주 하도록 만들어 중간 중간 revoke 할 수 있는 기회를 제공하는 형태가 된다.

    엑세스 토큰의 누출에서 오는 위험을 줄이고자 할 때 많이 사용하곤 하는데 개발 단계에서는 refresh 로직을 직접 작성하는 것이 귀찮기도 하고 매번 로그인을 새로해야 하기 때문에 유효기간을 늘려서 사용하는 경우가 많이 있다.

     

    short-lived access token + no refresh token

    애플리케이션의 세션 기간 혹은 몇 주 정도의 기간만큼의 유효기간을 설정하여 사용하는 방식을 말한다.

    이 경우에는 엑세스 토큰이 만료되면 사용자에게 재인증을 요구하게 된다. 즉 로그인 창이 새롭게 다시 등장하게 된다.

    이렇게 일정 시간이 지나면 사용자에게 재인증을 요구함으로써 엑세스 토큰이 탈취당했을 경우 발생하는 위험을 유효기간이내로 제한시키는 효과를 얻을 수 있다.

    리프레시 토큰이 없기 때문에 사용자가 실제로 사용하고 있는 동안이 아니라면 엑세스 토큰을 사용할 수 없다. 남아있는 유효기간 동안 사용할 수 있겠지만 만료가 된다면 사용자가 재인증 하지 않는 이상 토큰을 갱신해서 사용할 방법이 없기 때문이다.

    만약 주기적으로 데이터를 동기화해야 하는 것과 같이 백그라운드에서 작업이 필요한 경우에는 적합한 방법은 아니다.

Designed by Tistory.