로그인? 난 부럽지가 않어

EntryDSM·2022년 7월 13일
108

안녕하세요! 대덕소프트웨어마이스터고등학교의 입학전형 시스템의 백엔드 개발자 2학년 김범진입니다!
저는 언제나 새로운 것을 배우고 끊임없이 도전하는 것을 즐기며 개발을 하고 있습니다. 글 내용 중 오류가 있거나 저에게 궁금한 점이 있다면 댓글 또는 sdpthf@naver.com으로 연락해주세요.


OAuth가 뭐길래..

OAuth는 Open Authorization의 줄임말입니다. 이는 말 그대로 개방된 권한을 뜻합니다.

인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준이다. (위키백과)

간단히 말해서, OAuth는 로그인을 다른 웹사이트로 위임하는 사용자 인증 방식입니다.

매번 회원가입과 로그인 기능을 구현하면 귀찮을 뿐만 아니라 보안상 취약점이 존재할 가능성이 큽니다.
이를 구글이나 카카오처럼 큰 회사에서 잘 만들어놓은 로그인을 통해 사용자를 인증한다면 믿을 만할 것 같은데요?

지금부터 OAuth에 대해서 살펴보겠습니다.

OAuth 2.0 파보기

용어 정리

OAuth에 대해서 톺아보기 전에 사용되는 용어에 대한 정리부터 해보겠습니다.

  • Authentication : 인증이라는 뜻으로, 접근 자격이 있는지 검증하는 단계를 말합니다.

  • Authorization : 인가라는 뜻으로, 자원에 접근할 권한을 부여하는 것입니다. 인가가 완료되면 리소스 접근 권한이 담긴 Access Token이 Client에게 부여됩니다.

  • Resource Server : OAuth2 서비스를 제공하고, 자원을 관리하는 서버입니다.

  • Resource Owner : Resource Server의 계정을 소유하고 있는 사용자를 의미합니다.

  • Client : Resoure Server에 접속해서 정보를 가져오고자 하는 애플리케이션입니다. (쉽게 말해 내가 만든 서버를 의미합니다.)

  • Client App : Resource Owner의 요청을 도와 서버로 요청을 보내는 앱 또는 웹 애플리케이션입니다. (프론트엔드, 안드로이드, iOS 애플리케이션을 의미합니다.)

  • Authorization Server : 인증/인가를 수행하는 서버로, Client의 접근 자격을 확인하고 Access Token을 발급하여 권한을 부여하는 역할을 수행합니다.

  • My Access Token : 이 글에서는 Client에서 발급되어 Client의 서비스를 사용할 수 있도록 해주는 access token을 My Access Token이라 부르겠습니다.

  • Access Token : Authorization Server에서 발급된 토큰으로, Resource Server에서 Resource Owner의 보호된 자원을 획득할 때 사용되는 만료 기간이 있는 토큰입니다. (자체 로그인 기능을 개발하면 Client가 Authorization Server이자 Resource Server가 됩니다)

  • Refresh Token : Access Token 만료시 이를 갱신하기 위한 용도로 사용하는 토큰입니다. Refresh Token은 일반적으로 Access Token보다 만료 기간이 깁니다.

OAuth의 흐름

OAuth 인증 방식은 여러 가지 방법이 있지만, 가장 대표적으로 사용되는 Authorization Code Grant 방식에 대해 자세히 알아보겠습니다. (다른 방식이 궁금하다면 여기를 참고해주세요.)

느낌 잡기

Client가 Authorization Server와 Resource Server를 이용하기 위해서 사전 승인을 받습니다.

  1. Resource Owner가 Client 서비스 이용을 위해 Client로 로그인 요청을 보냅니다.
  2. Client가 Authorization Server로 Redirect 시켜 Resource Owner가 Authorization Server로 로그인을 할 수 있도록 합니다.
  3. 로그인이 완료되면 Authorization Server에서 Client로 인증 허가되었다는 의미로 Authorization Code를 발급합니다.
  4. Client는 Authorization Code를 이용해 Authorization Server로부터 Access Token과 Refresh Token을 받습니다.
  5. Resource Owner의 정보가 필요할 경우 해당 토큰을 통해 Resource Server로부터 정보를 가져옵니다.
  6. Access Token이 만료된다면, Refresh Token을 통해 재발급 받습니다.

자, 이제 전반적인 흐름에 대해 파악하였다면 각 단계에 대해 조금 더 자세하게 알아볼까요?

OAuth 서비스 중 구글을 이용한다고 가정해봅시다. 그리고 대부분 사용자 정보를 데이터베이스에 저장하기 때문에 서버에서 인증 작업을 하는 것을 기준으로 하겠습니다.

OAuth API 사용

사전 작업

  1. Google OAuth를 사용하기 위해서는 가장 먼저 프로젝트를 생성해야 합니다.
  2. 몇가지 동의를 한 후 우리의 서비스 URI를 등록합니다.
    '승인된 리디렉션 URI'의 URI에는 사용자가 인증을 한 후 리디렉션 될 주소를 적으면 됩니다.
    (ex http://localhost:8080/auth)
  3. OAuth Client를 생성하면 Client ID와 Client Secret을 받을 수 있습니다. OAuth를 사용할 때 중요한 정보이니 잘 보관합니다.

자세한 예시

  1. 처음 요청을 보내는 URL은 https://accounts.google.com/o/oauth2/v2/auth 입니다.
    해당 URL 뒤에 여러 파라미터를 더해 요청을 보내야 합니다.
  • client_id : 처음 등록할 때 받은 Client ID
  • redirect_uri : 사전에 등록한 리디렉션 될 주소
  • response_type : 요청에 대한 응답 타입(Authorization Code Grant 방식을 이용하기 때문에 'code'를 입력)
  • scope : Resource Server(구글)로부터 받아올 정보의 범위(ex. 이메일, 공개된 모든 정보)

필수적으로 요청을 보내야 하는 이 4가지 외에도 access_type, state, include_granted_scopes 등 다양한 파라미터가 있습니다.

이제 OAuth API를 사용하는 대략적인 Flow에 대해 설명 드리겠습니다.

  1. Authorization Server(구글)에서 반환한 URI로 Client App(프론트엔드)에서 요청을 보냅니다.
  2. Resource Owner(사용자)가 구글 아이디와 비밀번호를 입력해 Authorization Server(구글)에 로그인 요청을 보냅니다.
  3. Authorization Server(구글)에 인증이 완료되면 Authorization Server(구글)에서 리디렉션 URI로 https://localhost:3000/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7 와 같이 승인 코드를 반환해줍니다. 4/P7q7W91a-oMsCeLvIaQm6bTrgtp7이 바로 인코딩된 코드값입니다.
  4. Client(개인 서버)에서 인코딩된 코드값을 디코딩한 후 Resource Owner(사용자)의 정보를 받아오기 위한 토큰으로 바꾸어야 합니다. https://oauth2.googleapis.com/token 로 여러 파라미터와 함께 요청을 보냅니다.
  • client_id : 처음 등록할 때 받은 Client ID
  • client_secret : 처음 등록할 때 받은 Client Secret
  • grant_type : 요청에 대한 응답 타입(Authorization Code Grant 방식을 이용하기 때문에 authorization_code를 입력)
  • code : 디코딩한 코드값
  • redirect_uri : 사전에 등록한 리디렉션 될 주소
  1. 이제는 토큰을 이용해 Resource Owner의 정보를 가져올 차례입니다. 아까 받은 1/fFAGRNJru1FTz70BzhT3Zg와 같은 토큰을 매개변수로 넣어 https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=1/fFAGRNJru1FTz70BzhT3Zg 처럼 요청을 보냅니다.
  2. 아까 설정한 scope에 따라 Resource Server(구글)로부터 정보를 받아옵니다. 해당 토큰을 다시 사용할 일이 있다면 별도의 저장소(redis 등)에 저장을 하면 되고, 1회성으로 Resource Owner(사용자)의 정보를 받아오고 끝낸다면 별도로 저장하지 않습니다. Resource Owner(사용자)가 우리 서비스를 사용할 수 있도록 별도의 토큰(My Access Token)을 만들어 반환해야 합니다.

이렇게 하면 Resource Owner가 직접 회원가입을 하지 않고도 로그인을 할 수 있고 Resource Server(구글)에서 받아온 정보를 DB에 입력해 활용할 수 있습니다.

이제 정말 OAuth를 통한 로그인 끝 !!

인줄 알았죠? 😱
하지만 매우 아쉽게도 Authorization Code Grant 방식에는 몇가지 문제점이 존재합니다.

  1. 중간에 Authorization Server(구글)로부터 받은 인증 코드를 탈취당할 수 있는 취약점이 있습니다.
  2. 구글로부터 미리 발급받은 Client Secret을 앱 또는 웹 어딘가에 저장해야 합니다. Client App에서 저장해야한다는 말은 탈취될 가능성이 있다는 것을 의미합니다.

그래서 뭘 어떻게 하라는 말이에요오.. 🤔


OAuth 2.0 PKCE

PKCE(Proof Key for Code Exchange)란?
: 동일한 컴퓨터에 침입하는 악성 프로그램이 인증 코드를 가로채는 것을 방지하기 위한 모바일 장치의 공용 클라이언트용 OAuth 2.0 보안 확장이다. (loginradius)

PKCE는 간단히 말해서 Authorization Code Grant 방식에서 Client Secret을 이용하던 것을 일회용 암호를 이용하는 것으로 변경한 것입니다.

PKCE(Proof Key for Code Exchange)로 알려진 인증을 받기 위한 코드를 교환할 때 증명할 수 있는 키를 사용하는 방식은 악의적인 공격을 방지하고 Authorization Code Grant 방식을 안전하게 수행하기 위한 방법입니다.

주로 Client Secret을 사용하는 클라이언트 애플리케이션이나 Authorization Code Grant 방식에 사용되는 Client Secret을 대체하기에 유용합니다.

이 방식은 기본적으로 code_verifier, code_challenge, code_challenge_method 라는 세 개의 매개 변수와 함께 동작합니다.

말로 설명하는 것보다 사진을 보는 것이 더 이해가 잘 될 것입니다. 아래 사진에 Authorization Code Grant + PKCE 방식이 잘 나타나있습니다.

  • code_verifier : 48 ~ 128 글자수를 가진 Random String이다. A-Z, a-z, 0-9, -._~ 문자들로만 구성됩니다.
    ex) code_verifier=EAp91aanXdoMcoOc2Il55H3UDDIV909k9olEEcl6L24J6_9X
  • code_challenge : 선택한 해싱 알고리즘으로 Code Verifier를 해싱한 후 Base64로 인코딩을 한 값입니다. -> Base64Encode(Sha256(code_verifier))
    ex) code_challenge=HVoKJYs8JruAxs7hKcG4oLpJXCP-z1jJQtXpQte6GyA
  • code_challenge_method(선택) : 코드 챌린지를 해싱하는데 사용한 메서드를 표기합니다. 기본값은 'plain'입니다.
    ex) code_challenge_method=S256

PKCE 방식은 기본적으로 Authorization Code Grant 방식의 보안적인 취약점을 보완하고 더 안전하게 수행할 수 있도록 합니다. Authorization Code Grant 방식에서 변경된 점은 다음과 같습니다.

  1. Client는 code_challenge, code_challenge_method와 함께 인증 요청을 보냅니다.
  2. Authorization Server는 code_challenge, code_challenge_method를 저장하고 Authorization Code를 발급합니다.
  3. Client는 code_verifier와 함께 액세스 토큰 요청을 보냅니다.
  4. Authorization Server는 code_challenge와 code_challenge_method를 이용해 code_verifier를 검증한 후 액세스 토큰을 발급합니다.

OAuth 공식 사이트에서 PKCE를 직접 체험해 볼 수 있다고 합니다. 아직 이해가 되지 않았거나 직접 체험해보고 싶은 사람들은 사이트에 방문해보면 좋을 것 같습니다.

마지막으로

이번 글에서는 OAuth의 용어와 흐름, 더 나아가 PKCE에 대해서 알아보았습니다. OAuth에 대한 기본적인 용어와 흐름에 대해 이해를 했다고 해서 바로 적용할 수 있는 것은 아닙니다. 요청을 보낼 때 필요한 요소들은 알려주었지만 구체적으로 어떻게 해야 하는지에 대한 언급은 없었습니다. 아쉽지만 이번 글은 OAuth에 대한 독자의 이해를 돕기 위함이 목적이기 때문에 적용하는 방법에 대해 하나씩 알려주지 않았습니다.

사용하고 있는 프레임워크에서 외부 API를 호출하는 방법, 토큰을 다루는 방법 등 외적으로 알아야 할 것들이 존재합니다. 따라서 해당 글을 보고 이해를 했다고 해서 방심하지 말고 직접 해보며 익혔으면 좋겠습니다 😊

참고

OAuth 정의 : https://ko.wikipedia.org/wiki/OAuth
OAuth 용어 : https://blog.naver.com/mds_datasecurity/222182943542
OAuth 흐름 : https://bravenamme.github.io/2019/10/25/oauth2.0

PKCE 정의 : https://www.loginradius.com/blog/engineering/pkce/ , https://datatracker.ietf.org/doc/html/rfc7636
PKCE 용어 : https://juniortech.tistory.com/15
PKCE 흐름 : https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-proof-key-for-code-exchange-pkce

profile
대덕소프트웨어마이스터고등학교의 입학전형시스템을 개발하고 있는 팀 EntryDSM입니다. https://velog.io/@entrydsm 에 이어 전공 지식을 공유하기 위해 블로그를 개설하게 되었습니다.

5개의 댓글

comment-user-thumbnail
2022년 7월 13일

와 정말 유익해요💋

답글 달기
comment-user-thumbnail
2022년 7월 20일

감사합니다!

답글 달기
comment-user-thumbnail
2023년 11월 23일

이미지 발표자료에 사용할게요~
감사합니다~

답글 달기
comment-user-thumbnail
2024년 2월 26일

In a world filled with countless gaming experiences vying for attention, kinitopet stands out as a shining beacon of creativity and innovation.

답글 달기