OAuth2.0 이해

과녁스·2022년 2월 21일
0

Extra

목록 보기
3/4
post-thumbnail

개요


OAuth2.0에 대한 학습한 내용을 정리해보았습니다.

OAuth2.0


OAuth2.0(Open Authorization 2.0, OAuth2)은 인증을 위한 개방형 표준 프로토콜로써, Third-Party 프로그램에게 리소스 소유자를 대신하여 리소스 서버에서 제공하는 자원에 대한 접근 권한을 위임하는 방식을 제공합니다.

OAuth 2.0 주요 용어📕

Authentication

인증, 접근 자격이 있는지 검증하는 단계

Authorization

인가, 자원에 접근할 권한을 부여하는 것. 인가가 완료되면 리소스 접근 권한이 담긴 Access Token이 클라이언트에 부여됩니다.

Acess Token

리소스 서버에게서 리소스 소유자의 보호된 자원을 획들할 때 사용되는 만료 기간이 있는 Token 입니다.

Refresh Token

Access Token 만료 시 갱신하기 위한 용도로 사용하는 Token. Refresh Token은 일반적으로 Access Token보다 만료기간을 길게 설정합니다.

OAuth2.0의 4가지 역할👷

Resource Owner

리소스 소유자 또는 사용자. 보호된 자원에 접근할 수 있는 자격을 부여해 주는 주체. OAuth2.0 프로토콜 흐름에서 클라이언트를 인증(Authorize)하는 역할을 수행합니다.

인증이 완료되면 권한 획득 자격(Authorization Grant)을 클라이언트에게 부여합니다. 개념적으로는 리소스 소유자가 자격을 부여하는 것이지만, 일반적으로 권한 서버가 리소스 소유자와 클라이언트 사이에서 중개 역할을 수행하게됩니다.

Client

보호된 자원을 사용하려고 접근 요청을 하는 어플리케이션.

Resource Server

사용자의 보호된 자원을 호스팅하는 서버입니다.

Authorization Server

권한 서버로써 인증/인가를 수행하는 서버로 클라이언트의 접근 자격을 확인하고 Access Token을 발급하여 권한을 부여하는 역할을 수행합니다.

권한 부여🔐

Authorization Code Grand(권한 부여 승인 코드 방식)

권한 부여 승인을 위해 자체 생성한 Authorization Code를 전달하는 방식으로 많이 쓰이고 기본이 되는 방식 입니다.

간편 로그인 기능에서 사용되는 방식으로 클라이언트가 사용자를 대신하여 특정 자원에 접근을 요청할 때 사용되는 방식입니다. 보통 타사의 클라이언트에게 보호된 자원을 제공하기 위한 인증에 사용됩니다. Refresh Token의 사용이 가능한 방식입니다.

권한 부여 승인 요청 시 response_type을 code로 지정하여 요청합니다. 이후 클라이언트는 권한 서버에서 제공하는 로그인 페이지를 브라우저를 띄워 출력합니다. 이 페이지를 통해 사용자가 로그인을 하면 권한 서버는 권한 부여 승인 코드 요청 시 전달받은 redirect_url로 Authorization Code를 전달합니다. Authorization Code는 권한 서버에서 제공하는 API를 통해 Access Token으로 교횐됩니다.

권한 부여 승인 코드 방식

이미지 출처 : https://blog.naver.com/mds_datasecurity/222182943542

Implicit Grant(암묵적 승인 방식)

자격증명을 안전하게 저장하기 힘든 클라이언트(예: JavaScript 등의 스크립트 언어를 사용한 브라우저)에게 최적화된 방식입니다.

암묵적 승인 방식에서는 권한 부여 승인 코드 없이 바로 Access Token이 발급됩니다. Access Token이 바로 전달되므로 만료기간을 짧게 설정하여 누출의 위험을 줄일 필요가 있습니다.

Refresh Token 사용이 불가능한 방식이며, 이 방식에서 권한 서버는 client_secret를 사용해 클라이언트를 인증하지 않습니다. Access Token을 획득하기 위한 절차가 간소화되기에 응답성과 효율성은 높아지지만 Access Token이 URL로 전달된다는 단점이 있습니다.

권한 부여 승인 요청 시 response_type을 token으로 설정하여 요청합니다. 이후 클라이언트는 권한 서버에서 제공하는 로그인 페이지를 브라우저에 띄워 출력하게 되며, 로그인이 완료되면 권한 서버는 Authorization Code가 아닌 Access Token을 redirect_url로 바로 전달합니다.

암묵적 승인 방식

Resource Owner Password Credentails Grant(자원 소유자 자격증명 승인 방식)

usesrname, password로 Access Token을 받는 방식입니다.

클라이언트가 타사의 외부 프로그램일 경우 이 방식은 적용하면 안됩니다. 자신의 서비스에서 제공하는 어플리케이션인 경우에만 사용되는 인증 방식입니다. Refresh Token의 사용도 가능합니다.

제공하는 API를 통해 username, password를 전달하여 Access Token을 받는 것입니다. 중요한 점은 이 방식은 권한 서버, 리소스 서버, 클라이언트가 모두 같은 시스템에 속해 있을 때 사용되어야 하는 방식입니다.

자원 소유자 자격증명 승인 방식

Client Credentials Grant(클라이언트 자격증명 승인 방식)

클라이언트의 자격증명만으로 Access Token을 획득하는 방식입니다.

OAuth2.0의 권한 부여 방식 중 가장 간단한 방식으로 클라이언트 자신이 관리하는 리소스 혹은 권한 서버에 해당 클라이언트를 위한 제한된 리소스 접근 권한이 설정되어 있는 경우 사용됩니다.

이 방식은 자격증명을 안전하게 보관할 수 있는 클라이언트에서만 사용되어야 하며, Refresh Token은 사용할 수 없습니다.

클라이언트 자격증명 승인 방식

요청/응답 예제

client_id, client_secret

클라이언트 자격증명. 클라이언트가 권한 서버에 등록하면 발급받을 수 있으며 권한 서버 연동 시 클라이언트의 검증에 사용됩니다.

redirect_url

권한 서버가 요청에 대한 응답을 보낼 url을 설정합니다.

response_type

권한 부여 동의 요청 시 포함되는 값으로 권한 부여 방식에 대한 설정입니다. 아래 값 중 한 개를 사용합니다.

  • code: Authorization Code Grant
  • token: Implicit Grant

state

CSRF 공격에 대비하기 위해 클라이언트가 권한서버에 요청 시 포함하는 임의의 문자열. 필수 사항은 아니지만 클라이언트가 요청 시 state를 포함 시켰다면 권한 서버는 동일한 값을 클라이언트에게 보내야 합니다.

grant_type

Access Token 획득 요청 시 포함되는 값으로 권한 부여 방식에 대한 설정입니다. 아래 값 중 한 개를 사용합니다.

  • authorization_code: Authorization Code Grant
  • password: Resource Owner Password Credentials Grant
  • client_credentials: Client Credentials Grant

code

Authorization Code Grant 방식에서 Access Token요청 시 사용됩니다. 권한 서버에서 획득한 Authorization Code를 입력합니다.

token_type

발행된 Token의 타입. 대표적으로 Bearer, MAC(Message Authentication Code)가 있습니다.

expires_in

토큰 만료 시간(단위: 초)

example_parameter

Token 타입에 따른 추가 파라미터

추가로 API 요청에 포함되는 Authorization Basic 헤더는 Client 자격증명 관련 데이터로 client_id와 client_secret값을 아래와 같이 Base64 인코딩하여 생성합니다.

취약점 대응


OAuth2.0의 대표적인 취약점으로 CSRF와 Covert Redirect가 있습니다.

CSRF

기본적인 SNS 연동을 통한 flow는 아래와 같습니다.

  1. 기존 계정과 SNS 계정 연동 요청
  2. 요청 SNS 로그인 페이지 출력 (Client ID 값이 포함된 로그인 페이지)
  3. ID/PW 를 통해 SNS 계정에 로그인
  4. 로그인 성공 시 인증 서버로부터 Authorization code를 발급 받음 (Authentication Server -> 사용자)
  5. 발급 받은 code 값과 state 값을 Client 서버로 전송 (사용자 -> Client Server)
  6. code 값과 state 값 검증 후 Client 서버에 로그인 되어있는 계정과 SNS 계정이 연동됨

여기서 state 값은 CSRF token 역할을 하는데, 만약 state 값에 대해 검증이 누락되어 있거나 미흡할 경우 사용자 계정을 탈취할 수 있습니다.

5번 과정에서 Client 서버로 전송하는 내용을 추출하여 만든 CSRF 공격 페이지에 사용자(피해자)가 접근하면, 사용자(피해자) 계정과 공격자 계정이 연동되며, 공격자의 SNS 계정을 통해 사용자(피해자) 계정으로 로그인을 할 수 있습니다.

CSRF 참고 : CSRF 공격이란? 그리고 CSRF 방어 방법

Covert Redirect

OAuth 2.0 인증 Flow 중 redirect_uri 파라미터 값에 대해 검증이 누락되거나 미흡할 경우 발생하는 취약점입니다.

정상적인 경로라면 사용자(Resource Owner)가 로그인 성공 후 발급받은 Authorization code를 Client로 전달해야합니다.

그러나 공격자는 사용자(피해자)에게 변조된 Redirect URI를 보내 로그인을 유도합니다. 4번 단계에서 사용자(피해자)가 Redirect URI 값이 변조된 URL로 로그인할 경우, Authorization code 값이 공격자 서버로 전달되어 공격자는 사용자(피해자)의 계정을 탈취할 수 있습니다.

Covert Redirect

이미지 출처 : https://meetup.toast.com/posts/105

대응 방안은 Authorization Server 에서 Redirect URI 값에 대해 Full Path 검증을 진행해야합니다.

만일 도메인까지만 검증하거나 일부 경로까지만 검증하도록 조치할 경우, Redirect 취약점이 존재하는 Client는 여전히 Authorization code를 탈취할 수 있습니다.

토큰 유출 문제 해결


토큰 유출

서버측에서는 토큰을 갖고 있는 클라이언트가 정말 클라이언트 본인이 맞는지 확인할 수 없다는 문제점이 있다.

그래서 이에 대한 보안 대책으로 리프레쉬 토큰이라는 추가적인 토큰을 활용할 수 있다. 이 리프레쉬 토큰은 사용자 인증이 아닌 새로운 액세스 토큰을 생성하는 용도로만 사용된다. 그러면 왜 굳이 별도의 토큰을 두고 새로운 액세스 토큰을 발급받도록 한 것일까? 이는 위의 JWT 유출 문제를 다음처럼 해결하기 위한 것이다.

  • Access Token의 유효 기간을 짧게 설정한다.
  • Refresh Token의 유효 기간은 길게 설정한다.
  • 사용자는 Access Token과 Refresh Token을 둘 다 서버에 전송하여 전자로 인증하고 만료됐을 시 후자로 새로운 Access Token을 발급받는다.
  • 공격자는 Access Token을 탈취하더라도 짧은 유효 기간이 지나면 사용할 수 없다.
  • 정상적인 클라이언트는 유효 기간이 지나더라도 Refresh Token을 사용하여 새로운 Access Token을 생성, 사용할 수 있음.

Refresh Token 탈취

그런데 이 Refresh Token 자체가 탈취당한다면 어떻게 할까? 공격자는 이 토큰의 유효 기간만큼 다시 Access Token을 생성해서 다시 정상적인 사용자인 척 위장할 수 있다. 그렇기 때문에 여기서는 서버측의 검증 로직이 필요한데 스택오버플로우의 답변을 보면 다음과 같은 방법을 제안하고 있다.

  • 데이터베이스에 각 사용자에 1대1로 맵핑되는 Access Token, Refresh Token 쌍을 저장한다.
  • 정상적인 사용자는 기존의 Access Token으로 접근하며 서버측에서는 데이터베이스에 저장된 Access Token과 비교하여 검증한다.
  • 공격자는 탈취한 Refresh Token으로 새로 Access Token을 생성한다. 그리고 서버측에 전송하면 서버는 데이터베이스에 저장된 Access Token과 공격자에게 받은 Access Token이 다른 것을 확인한다.
  • 만약 데이터베이스에 저장된 토큰이 아직 만료되지 않은 경우, 즉 굳이 Access Token을 새로 생성할 이유가 없는 경우 서버는 Refresh Token이 탈취당했다고 가정하고 두 토큰을 모두 만료시킨다.
  • 이 경우 정상적인 사용자는 자신의 토큰도 만료됐으니 다시 로그인해야 한다. 하지만 공격자의 토큰 역시 만료됐기 때문에 공격자는 정상적인 사용자의 리소스에 접근할 수 없다.

중요한 것은 발급된 토큰 자체는 그냥 그 JWT 문자열 자체로 존재하는 것이기 때문에 클라이언트나 서버측에서 전역적으로 만료시킬 수 있는 개체가 아니다.

그렇기 때문에 토큰의 유효 기간이 지나기 전까지는 만료된 토큰을 NoSQL 같은 데이터베이스에 저장하여 관리할 필요가 있다.

만약 공격자가 Refresh Token을 탈취해서 정상적인 사용자가 Access Token을 다시 발급받기 전에 자기가 먼저 Access Token을 생성한다면 어떻게 될까? 이 경우에도 Access Token의 충돌이 일어나기 때문에 서버측에서는 두 토큰을 모두 폐기(만료 대신 폐기가 어울리는 용어인 것 같다)해야 할 것이다.

그래서 ietf 문서에서는 아예 Refresh Token도 Access Token과 같은 유효 기간을 가지도록 하여 사용자가 한 번 Refresh Token으로 Access Token을 발급받았으면 Refresh Token도 다시 발급받도록 하는 것을 권장하고 있다.

그럼 만약에 공격자가 Access Token, Refresh Token을 둘 다 탈취한다면 어떻게 할까? 이 때는 방법이 없다. 프론트엔드나 백엔드 로직을 강화하여 토큰이 유출되지 않도록 보완하는 수 밖에 없을 것이다.

출처🙏

profile
ㅎㅅㅎ

0개의 댓글