본문 바로가기

백엔드 개발/Spring-Security

[01]-OAuth2 이론

목차
1. OAuth2의 등장 배경 (OAuth2란 왜 필요한가?)
2. 그래서 OAuth2란?
3. OAuth2 관련 용어 정리 
4. OAuth2의 동작 과정 

1. OAuth의 등장 배경  (OAuth2란 왜 필요한가?)

현 IT 서비스 시장에는 구글이나 아마존, 트위터와 같은 메가급 기업들이 다수 존재한다. 이 기업의 사용자들은 수천만 많게는 억대 단위로 넘어간다. 그 만큼 해당 기업들이 가진 리소스도 다수 존재할 것이다. 하지만, 단체생활을 해봤다면 알 수있듯이 하나의 일을 작업자 전원이 만족하는 방식으로 수행한다는 것은 불가능에 가깝다. A는 서류 작업을 더 완벽히 하고 다음 일로 갔으면 좋겠고, B는 그저 본 작업을 빨리 진행하고 싶을 수도 있다. 이는 우리가 특정 서비스를 이용할 때도 마찬가지이다. A는 구글 캘린더에 더 많은 기록이 가능하고, 데스크톱 앱 형태로 컴퓨터에 내장하고 쓰고 싶을 수 있다. B는 구글 피트니스 데이터가 리포트 형태로 뜨길 원한다. 지금의 형태는 보는 입장에서 너무 투박하기 때문이다. 
  하지만 기업에서 제공할 수 있는 좋은 퀄리티의 서비스는 경제적으로 한정되어 있다. 이에 많은 메가급 기업들이 제 3자에게 자신들의 Resource를 가져다 쓸 수 있도록 창고문을 열어놓는다. 이것이 기업별 API일 것이다. 
  이런 방식으로 사용자들에게 더 좋은 서비스를 제공하려 한다는 것을 알겠지만, 여기도 문제가 있다. 예를 들어 A라는 사람의 구글 피트니스 데이터를 가져오려면, 해당 A라는 사람의 접근권한 즉 ID와 PASSWORD가 필요하게 된다. 일반적으로는 이런 식의 방법을 생각할 수도 있겠다. 

이런 형태로 로직을 짠다면 분명 Third-party인 우리 앱이 구글 API를 이용해 사용자에게 맞춤 서비스를 제공할 수 있을 것이다. 하지만 다음과 같은 치명적인 문제가 생긴다. 

정말 치명적인 문제다. 사용자들은 PASSWORD를 다른 사이트에서도 돌려쓰는 경우가 많기에 피해규모가 더 커질 수 있다. 따라서 위와 같은 경우 사용자, 제 3자 서비스, 구글 모두에게 부담이 크다. 사용자는 자신의 개인정보를 아직 신뢰가 가지 않는 사이트에 맡겨야 한다는 부담감이 생기고, 제 3자 서비스는 해당 개인정보를 DB에 가지고 있어야 한다는 위험 부담이 생긴다. 알다시피 메가급 기업이 아니므로 보안도 그만큼 강하지 못할 것이다. 구글은 자신들의 개인정보를 제 3자 서비스들이 전부 가지고 있다는 것이 염려될 것이다. 

(1) 이를 막기 위한 기업들의 몸부림

기업들은 자신들의 API를 이용한 서비스를 장려하면서도 이러한 문제를 막기 위해 저마다 해결책을 내놓았다. 구글의AuthSUb, Yahoo의 BBAuth 가 대표적이다. 이러한 프로토콜의 원리는 OAuth2와 마찬가지로, 제 3자 App에서 기업으로 유효한 인가코드를 기업에게 보내면, 해당 기업은 자신의 API에 접근할 수 있는 접근토큰을 돌려주어 기업 내 리소스에 접근할 수 있도록 하는 것이다. 하지만 해당 해결책들은 어떠한 표준을 가지고 만들어진 것이 아니었다. 따라서 만약 제 3자 앱에서 복수의 기업 API를 쓰려한다면, 각 기업별 정책에 맞게 코드를 다시 짜야했다. 한 마디로 유지보수에 극악이었다. 

(2) OAuth1.0의 등장

이러한 문제의 해결책으로 등장한 것이 권한위임 프로토콜인 OAuth1.0이다. OAuth1.0은 트위터와 Ma.gnolia가 주도적으로 개발하였다. 하지만 OAuth1.0은 모바일 어플리케이션 환경에서는 안전을 보장하지 못하는 사례들이 등장하기 시작했다. 그래서 이러한 기존 사례들을 보완하고 1.0보다 좀 더 프로토콜 과정을 단순화한 OAuth2.0이 2012년에 등장하였다. ㅇ

2. 그래서 OAuth2란? 

OAuth2의 원리는 밑에서 더 뜯어보겠지만, 큰 그림은 위의 그림들과 같다. 

  OAuth2의 원리는 자세히 뜯어봐도 쉽지만, 그것을 설명하는 용어들 중 헷갈리는 것이 조금 있다. 따라서 해당 용어들을 먼저 살펴보고, 다음으로 정확한 원리에 대해 넘어가겠다.  

3. OAuth2에 나오는 용어 정리 

위에서는 사용자, 제 3자앱, 구글 이렇게 소개 했지만 이것을 OAuth2 문서에 나오는 전문적인 용어로 바꾸어 설명하겠다. 

(1) Resource Owner 

어떠한 리소스의 소유권이 있는 주체이다. 박광덕이라는 사람이 구글 캘린더에 자신이 3월달에 할 일들을 쭉 써놨다고 생각해보자. 그러면, 구글 캘린더에 있는 그 3월 할 일 목록 리소스의 소유권은 누구에게 있나? 구글? 아니다 박광덕씨한테 있다. 따라서 박광덕씨가 Resource Owner이다. 
흔히 우리가 말하는 사용자를 떠올리면 될 것 같다. 

(2) Authorization & Resource Server 

Authorization Server란, 접근 권한을 Client에게 주겠다는 Resource Owner를 인증하고, Client에게 AccessToken을 발급해주는 서버이다. 
Resource Server란, 리소스 즉 데이터를 가지고 있는 서버이다. 

기업에 따라서 이 두 과정을 하나의 서버 내에 구성하고 있는 경우도 있다. 

(3) Client

사람들이 많이 헷갈리는 부분 중 하나일 것 이라고 생각이 든다. 왜냐하면 Client의 말 뜻이 의뢰인이라 제 3자 앱의 사용자를 지칭한다고 여기기 쉽기 때문이다. 하지만 CS에서 배웠던 Client와 Server의 관계를 다시 떠올려보자. Client는 데이터를 요청하는 주체를 의미하고, Server는 요청 받은 데이터를 주는 주체를 의미했었다. 

이걸 OAuth2 프로토콜에 대입 해보자. 구글이나 카카오 같은 대기업의 리소스를 달라고 하는 주체가 누구였는가?? Resource Owner (박광덕 씨) 였던가?? 아니다!! 리소스를 달라고 한 주체는 우리가 만들 제 3자 APP 이었다!! 따라서 클라이언트는 제 3자 APP을 뜻한다. 서버는 위에서 말했듯이 데이터를 주는 주체(구글, 트위터 등)이다. 

(4) Redirect URI

위의 OAuth2 과정을 하기 위해서는 꼭 필요한 절차가 있다. 바로 Authorization Server가 인가코드를 전달할 RedirectURI를 미리 Server측에 등록해놓는 절차이다. 추후에 설명하겠지만 Resource Owner가 권한 위임을 인증하면 Server는 Client에게 인가코드 (우리 고객이 너한테 권한 위임해줘도 좋다 했으니까 '임시 허가증' 줄게의 "임시 허가증")를 보내줘야 한다. 그리고 RedirectURI는 임시 허가증을 받을 Client의 주소 이다. 이 주소를 미리 등록해놓는 이유는, Client와 미리 협의 되지 않은 주소로 허가증을 보내는 경우를 없애서 중간에 허가증을 탈취하는 것을 막기 위함이다. 
  RedirectURI는 기본적으로 보안을 위해 Https만 허용되지만, Local 환경 테스트를 위해 LocalHost의 경우 예외적으로 http가 허용된다. 

(5) Client ID, Client Secret

자, 당신이 정부 사업권을 따낸 개인 사업자라고 하자. 그러면 정부에서는 자신들과 사업을 할 사업자를 일반인들과 구분하기 위해서 당신에게 정부 사업허가 증서를 부여할 것이다.
  Client ID와 Client Secret 또한 똑같다. 구글이나 카카오에 Client (우리들이 만들 제 3자 APP)을 등록 시켜 놓으면, Server에서는 Client ID와 Client Secret을 우리에게 준다. (4)번의 RedirectURI가 인가코드라는 임시 허가증을 받는 과정이라고 설명 드렸다. 이제 우리가 data에 바로 접근할 수 있는 accessToken(진짜 허가증)을 받기 위해서 ClientID, Client Secret (개인 사업자 등록증, 정부 사업 허가 증서) 와 인가 코드 (임시 허가증)을 Server에 제출하면 된다.  

 

4. OAuth2의 동작과정

hudi님 블로그에 정리가 잘 되어 있었다. 이것을 나만의 방식으로 정리해보겠다. 과정 하나하나 정리해보겠다.

hudi님 블로그 참조! 감사합니다!

(1) 로그인 요청 (Resource Owner  >> Client(Front))

Client(우리 앱)를 사용하고자 하는 Resource Owner가 Client의 메인 페이지에 들어왔다고 하자. 처음 들어왔다면 당연히 로그인이 되지 않은 상태이기 때문에 로그인을 해야한다. 여기서 Resource Owner가 '구글로 로그인 하기!'를 클릭했다면 Client에게 로그인 요청을 보낸 것이다.

(2) 로그인 요청 (Client(Front) >> Authorization Server)

이제 Client는 해당 정보를 가지고 있는 Server에게, 

'저기요, 저희 앱 고객님 A가 저보고 당신들 서버에 있는 자기 Data를 쓰라고 했어요. 그 전에 이 A씨가 진짜 당신들 server에 존재하는 사람인지 확인해보시겠어요?
만약에 A씨가 서버에 데이터를 가지고 있는 사람이라면, 제가 답변 주소를 알려드릴테니, 글로 임시 허가증을 보내주세요!
PS(여기 제 사업허가증이랑, server에서 쓰고 싶은 데이터의 범위, 답변 주소, 보내주실 답변의 양식 등을 동봉해서 보내드립니다.)'

라고 해야한다. 
뭐 말은 길어졌지만 하는 일은 다음과 같다.


a. Resource Owner가 '구글로 로그인 하기'를 눌렀다.

b. Client에서는 Authorization Server URL에 Client_id(사업 허가증), redirect_uri(답변 주소), Scope(쓰고 싶은 Data의 범위), response_type(보내주실 답변 양식)을 담아서 그 주소로 화면을 Redirect 시킨다.  (ResourceOwner가 서버랑 직접 인증하도록)


예를 들어 Authorization URL이 https://authorization-server.com/auth라면, Client는 다음과과 같이 URL를 써서 보내야 한다. 

https://authorization-server.com/auth?response_type=code &client_id=29352735982374239857 &redirect_uri=https://example-app.com/callback &scope=create+delete

(3) 로그인 페이지 제공 

(2)번을 통해 화면은 서버가 제공하는 화면으로 Redirect 되었다. 서버는 여기서 

와 같이 Resource Owner가 자신을 인증할 수 있도록 화면을 제공한다. 

(4) ResourceOwner가 ID/PW를 해당 페이지에 기록 후 제출 

ResourceOwner가 ID/PW를 써서 제출한다. 

(5),(6) 인가코드 발급하여 Redirect URL로 전송 

ResourceOwner가 자신이 Server의 고객임을 인증했다면 Server는 인가코드 (임시 허가증)을 발급하여 RedirectURL에 전송한다. (인가 코드를 주는 방식은 회사마다 다를 수 있는데, 구글의 경우에는 쿼리스트링 형태로 RedirectURL 뒤에 AuthorizationCode="" 해서 붙여서 준다.) 

이떄 AuthorizationCode는 AccessToken을 갖기 위한 임시 허가증이므로 수명이 되게 짧다(1~10분 이내)

(7) Client(BACK)이 인가코드, ClientID, Client Secret을 가지고 AccessToken을 요청 

이제 Client는 인가코드, Client ID, Client Sercret을 가지고 Server에 AccessToken을 요청한다. 

(8) AccessToken 발급

만약 7번의 사항들이 유효하다면 Server는 Client에게 AccessToken을 발급해준다. 이제 Client는 발급받은 AccessToken을 DB에 저장하고, Resource Server를 이용해 해당 유저를 위한 서비스를 전개하면 된다.
이때 Access Token은 유출되어서는 안된다. 따라서 제 3자가 가로채지 못하도록 HTTPS 연결을 통해서만 사용될 수 있다.

 

Authorization Code와 Access Token 교환은 token 엔드포인트에서 이루어진다. 아래는 token 엔드포인트에서 Access Token을 발급받기 위한 HTTP 요청의 예시이다. 이 요청은 application/x-www-form-urlencoded 의 형식에 맞춰 전달해야한다.  

POST /oauth/token HTTP/1.1
Host: authorization-server.com

grant_type=authorization_code
&code=xxxxxxxxxxx
&redirect_uri=https://example-app.com/redirect
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx

필수로 전달해야하는 매개변수를 살펴보자.

  • grant_type : 항상 authorization_code 로 설정되어야 한다. (참고)
  • code : 발급받은 Authorization Code
  • redirect_uri : Redirect URI
  • client_id : Client ID
  • client_secret : RFC 표준상 필수는 아니지만, Client Secret이 발급된 경우 포함하여 요청해야한다.

(9) 로그인 성공 

위 과정을 성공적으로 마치면 Client 측에서 ResourceOwner에게 권한 위임 절차가 성공적으로 끝났음을 알린다. 

(10) ~(13) Access Token을 이용한 Resource 접근 및 이를 이용한 서비스 전개 

이제 ResourceOwner가 Client에게 Resource를 필요로 하는 어떤 작업을 요청하면, Client는 전에 발급 받았던 AccessToken을 들고 ResourceServer에게 방문한다. 
ResourceServer는 Token 확인 후 Client가 필요로 하는, Data를 제공한다. 
Client는 해당 데이터를 통해 자사의 서비스를 제공한다. 

 

5. 인가코드라는 개념이 존재하는 이유? 

아니 임시허가증을 발급하고 허가증을 다시 요청하는 절차를 왜 넣었을까? 그냥 ResourceOwner가 로그인 인증에 성공하면 Server에서 바로 RedirectURL 주소로 AccessToken을 주면 안되는 걸까? 먼저 위의 동작과정에서 살펴봤던 인가코드 받는 과정에 대해 살펴보자 

로그인 요청을 하면
프론트는 화면을 server의 인증 화면으로 redirect
인증에 성공하면 server는 다시 Client에게 인가 코드를 Redirect

보시다시피 두번의 Redirect 과정이 필수적이다. 왜냐하면, 해당 로그인 인증 과정은 ResourceOwner의 화면에 띄워져서 해당 사람이 눈으로 보며 직접 진행해야하는 작업이므로, Client Backend와 Server간에 내부적으로 일을 처리할 수가 없기 때문이다. 그런데 Redirect 과정에서는 누구에게나 그 내부 정보가 보임으로 AccessToken을 Redirect 자체에 실어 보내는 것은 큰 위험을 동반한다. 따라서 임시 허가 코드를 전달하는 것이다. 
이렇게 허가코드를 전달하면, Front는 다시 Back에게 그것을 전달하게 된다. 이 과정에서 탈취 당했다 해도, AccessToken을 얻기 위해서는 허가코드 외에 ClientID 나 ClientSecret 같은 다른 파라미터도 있어야 함으로 AccessToken을 탈취한 인가 코드로 발급받기는 쉽지 않을 것이다.
  그리고 AccessToken 발급 요청부터 시작하는 과정들은 일일히 ResourceOwner의 화면에 띄워져서 진행될 필요가 없기에, Backend에서 Server와 내부적으로 일처리하면 된다. 이렇게 되면 외부에 주요 정보들이 보이지 않게되므로 공격자가 AccessToken을 중간에서 가로챌 수 없게 된다.

'백엔드 개발 > Spring-Security' 카테고리의 다른 글

특화 프로젝트 Spring Security 분석  (0) 2024.04.26
JWT에 대하여  (0) 2024.04.25