OAuth라고 하면 대중적인 사례로 소셜로그인을 떠올린다.
카카오, 네이버, 구글로그인.. 님이 생각하는 그거 맞음
이번 포스팅에서는 OAuth가 무엇인지, OAuth2.0이 있기전의 OAuth1.0은 어땠는지 알아보려 한다.
일단 위키백과에서 정의한 OAuth의 정의는 다음과 같다.
OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준이다.
이게 무슨말인지 이해하기 위해서는 OAuth의 등장 배경을 알아볼 필요가 있다.
우리의 서비스가 사용자를 대신하여 구글의 캘린더에 일정을 추가하거나, 페이스북, 트위터에 글을 남기는 기능을 만들 수 있을 것이다.
우리 서비스가 구글에 접근하려면 사용자에 대한 인증과 인가를 받아야하는데, 이때, 가장 쉽게 이 기능을 구현하는 방법은 사용자로부터 구글 ID, Password 를 직접 제공받아 우리의 서비스에 저장하고 활용하는 방법이다.
하지만 이런 방법이 안전할까?
사용자들은 처음보는 우리 서비스를 신뢰하고 자신의 구글 계정 정보를 맡길 수 있을까? 사실 현재의 관점으로 바라보면 미친짓이나 다름없다.
또한 우리 서비스 입장에서도 유저의 다른 서비스의 id와 password를 관리하는것은 부담스러운 일이다. 자칫 잘못해서 보안 누출이 된다면 여러가지 고초를 겪을 수 있다.
또한 구글에서도 보안 수준을 아무리 높여도 외부에서 id랑 password가 노출된다면 공들이 보안조치가 무용지물이 된다. 자신의 사용자 정보를 신뢰할 수 없는 제3자에게 맡긴다는 것이 매우 불만족스러울 것 이다.
이러한 전쟁을 끝내줄 도구가 바로 OAuth다.
OAuth를 적용하면 어떻게 될까??
이전에는 우리의 서비스가 유저의 id와 password를 가지고 있었는데,
이제는 유저가 구글에 직접 id와 password를 입력해서 인증과정을 수행한다.
이 과정에서 우리의 서비스는 인증과정에 포함되지 않는다.
이 인증이 유효하다고 확인이 되면, 구글은 유저가 이용하고 싶은 기능에 대한 권한으로 우리의 서비스에게 access token을 발급한다.
이 토큰으로 우리의 서비스는 구글에 접근할 수 있는 것이다.
token을 이용하게 되면
1. 다른 서비스의 id 와 비밀번호가 아니다 라는 장점이 있다.
2. 다른 서비스가 갖고 있는 모든 기능이 아니라 그 중에 나의 서비스가 꼭 필요한 필수적인 기능만 부분적으로 허용한다.
그래서 OAuth는 인증은 유저가 직접하고, 권한은 서비스가 갖게하는 것이다.
그러니까 OAuth는 인증을 위한것이 아닌 인가를 위한 것!!
우리는 OAuth1.0에 대해 다루기 전에 인증과 인가의 개념을 살짝 짚고 넘어가자.
인증: authentication
사용자의 신원을 확인하는 행위
ex) 공항에서 신분증을 검사인가: authorization
인증 이후의 프로세스. 인증된 사용자가 어떠한 자원에 접근할 수 있는지를 확인하는 절차
ex) 비행기를 타기위해 비행기티켓을 보여줌
위의 예시에서도 보면, OAuth에는 3가지 역할이 있다.
이렇게 세 개의 역할이 나누어져서 상호작용을함으로써 Oauth 1.0 인가 프로토콜이 작동한다
Oauth client가 Oauth server 에게 Resource Owner가 어플리케이션 접근 의도가 있음을 API 호출을 통해서 알리게 된다.
그럼 그 응답으로 Oauth server는 Oauth client에게 리소스서버가 어느 웹 주소에서 인증을하고 Oauth client에 대한 인가 작업을 할 수 있는지 응답을 보내준다.
이 응답을 받은 Oauth client는 Resource Owner오너에게 302 리디렉트 또는 비슷한 웹 기반의 리드렉트 방식을 사용해서 리소스 오너를 Oauth server가 호스팅하는 웹사이트로 보내게 된다.
그렇다면 우리는 주로 아이디와 비밀번호를 입력을 하고, 여러가지 플로우를 탄 후에 마지막으로 어플리케이션에 대한 권한을 인가하게 된다.
이 인가 작업이 끝나면 또다시 Oauth server는 302 리디렉트 나 웹 기반 리디렉트를 사용을 해서 이렇게 받은 인증 값을 Oauth client에게 넘겨준다.
Oauth client는 이 인증 값을 받아서 API 호출을 통해서 Oauth server로부터 이 인가 권한을 나타내는 토큰을 넘겨받게 된다.
이후로 Oauth client는 내 리소스에 접근을 하거나 사용자를 대신해서 어떤 서비스에 접근을 할 때 이 토큰을 사용을 해서 리소스에 접근을 하게 된다.
내 아이디랑 비번이 한번도 Oauth client에게 전달된 적은 없었지만 정확하게 내 리소스에 대한 인증과 인가절차가 이루어졌다.
하지만 이 플로우에도 몇가지 문제가 있다.
scope에 대한 개념이 없음
: Oauth server의 어느 서비스까지 권한이 있는지? 사용자의 권한을 더 세밀하게 제어하기 어렵게 만든다.
유효기간에 대한 개념이 부족
: OAuth 1.0은 토큰의 유효기간을 관리하는 명세가 제공되지 않았다. 이로 인해 토큰이 무제한으로 유효할 수 있거나, 적절한 방법으로 만료 및 갱신되지 않을 수 있다. => 보안상의 위험이 있다.
클라이언트 구현이 복잡함
: 암호화 및 서명 절차, 요청 및 응답의 암호화 방식, Nonce 및 Timestamp 사용 등의 기술적인 요구사항들이 클라이언트 개발자에게 추가적인 부담을 주게 된다. 이는 애플리케이션 개발 및 유지보수의 어려움을 초래하고, 오류 발생 가능성을 높일 수 있다.
역할이 정확하게 나눠져 있지 않음
: OAuth 1.0 Server의 역할은 Resource Owner 인증 , 인가 토큰 발급 , 보호된 리소스 관리(호스팅)하는 역할을 담당하면서 역할이 모호함.
사용 환경이 제한적
: OAuth 1.0은 웹 브라우저 환경에 최적화된 프로토콜이다. => 모바일 애플리케이션에서 지원이 부족하다.
역시나 이런 문제를 해결하기 위해 2.0이 등장했다.
OAuth2.0은 1.0의 문제를 어떻게 해결했는지 알아보자.
1.0 에서는 토큰이라는 인가를 나타내는 값만 있으면 사용자의 모든 리소스에 접근 할 수 있었다.
ex) sns 사진 어플리케이션에 사진을 사용할 것을 인가했는데 내 동영상과 문서까지 접근을 하게됨
2.0 에서는 scope라는 기능을 추가해서 이 해당 토큰에 대해서 얼만큼의 접근 범위가 있는지를 나타낼 수 있게 되었다.
1.0에서는 보안성을 위해서 암호학적 기반 보안책들을 사용했다.
하지만 암호학적 보호를 받기 위해서는 signature를 만들어야 되는데, signature를 만들기 위해서는 http 메소드뿐만 아니라 uri 그리고 여러가지 파라미터, 그리고 파라미터 같은 경우에는 어떠한 파라미터를 넣어야 할 것인지 넣지 말아야 할 것인지 그렇다면 어떤 순서로 정렬을 할 것인지에 대해서 정확한 분류가 필요했다. 간단한 post 요청을 위해서도 매번 동일한 signature를 만들어야했다. => 그냥 겁나 번거롭고 복잡했다~ 라는 뜻
이 문제를 2.0에서는 Bearer token + TLS를 사용해서 극복했다.
📌 Bearer token
“Bearer”은 소유자라는 뜻인데, “이 토큰의 소유자에게 권한을 부여해줘”라는 의미로 이름을 붙였다고 함.
다른 암호화학적 보호나 클라이언트의 바인딩되는 장치들을 모두 배제를 한 채 해당 토큰을 소유를 하고 있는 것만으로도 이 토큰에 대한 사용권한이 있음을 인정해주는 토큰을 말한다.
토큰을 소유하는 것만으로도 권한이 생기기 때문에, 2.0에서는 TLS, 가장 대표적예로 Https 를 강제하게 된다.
1.0에서는 토큰의 유효기간이 굉장히 길었다. 이 문제는 만약에 토큰이 탈취되었을 때, 긴 기간 동안 어뷰징을 할 수 있는 가능성이 있다.
2.0에서는이 문제를 해결하기 위해서 Refresh token이라는 개념을 새롭게 만들었다. 리소스를 접근을 할 때는 짧은 유효기간을 가진 Access token을 사용하고 Refresh token으로 재발급을 받아서 사용한다.
이 방식을 사용하게 되면은 Access token이 만약에 탈취가 되더라도 이 Access token이 살아있는 짧은 기간만 어뷰징을 할 수 있기 때문에 1.0의 보안성을 개선할수있다.
OAuth 1.0에서는 OAuth Server가 “인증”과 “리소스 관리”를 전부 맡아서 하였지만, OAuth 2.0에서는 Auth server와 Resource Server를 분리함으로써 조금 더 개선된 아키텍쳐를 만들 수 있게 되었다.
이로 인해, 인증 서버는 인증을 담당하고, 리소스 서버는 자신이 가진 리소스를 관리하고 제어한다. 이를 통해 보안성이 강화되고, 클라이언트의 복잡성도 간소화 시킬 수 있게 된다.
위에서 1.0 의 사용환경이 제한적이라고 했다. 1.0은 웹브라우저 환경에서 작동하도록 최적화 되어있는데, 이는 다른 환경에서는 사용하기 어려운 프로토콜이다.
다시 한번 OAuth 1.0 동작에 대해 말하자면, 클라이언트 애플리케이션은 사용자를 인증화면으로 리디렉트시키기 위해 301 Redirect를 사용하며, 사용자의 인증이 완료되면 인증 코드를 클라이언트의 애플리케이션으로 리디렉트하기 위해 웹 브라우저의 리디렉트 방식을 활용한다.
이를 통해, OAuth 1.0은 웹 브라우저 환경에서의 인증과 권한 부여에 최적화된 프로토콜로 사용된다.
하지만, 이러한 “웹 브라우저에 최적화”된 프로토콜이 제한으로 다가오게 된다는 것이다. 현재 여러 서비스및 서버 구축에선 “Server-to-Server Interaction(서버 간 상호작용)”이 필수적으로 요하게 된다.
2.0은 이 문제를 해결하기 위해 여러가지 사용환경을 위한 Grant라는 개념을 추가했다.
Grant라는 것은 OAuth 2.0에서 여러가지 사용 환경에 대해 허가를 받는 유형이라 생각하고 넘어가자.
Grant는 authorization code, implicit grant, resource owner password credentials, client credentials 이렇게 주요한 네가지 유형이 있다.
1. Authorization Code Grant (인가 코드 방식)
Resource Owner에게 사용 허락을 받았다는 증서인 권한 코드 (Authorization Code)를 가지고 Access Token을 요청하는 방식이다.
여기서 권한 코드는 "리소스오너한테 사용 허락받음"을 증명하는 코드다.
Resource Owner에게 사용 허락을 받은 후 코드를 따로 받고, 이 코드를 가져가서 요청하는 방식이므로 다른 방식보다 조금 더 복잡하다.
대신 다른 방식보다 좀 더 신뢰성이 있는 방식이라 발급되는 Access token의 유효시간이 좀 더 길고, 다시 액세스 토큰을 발급받을 수 있는 Refresh Token을 함께 발급해 준다.
2. Implicit Grant (암묵적 승인 방식)
이 방식은 추가적인 절차 없이 Resource Owner가 인증 및 허가를 하면 바로 Access Token이 발급되는 방식이다.
위 방식에서의 권한 코드 없이 바로 Access Token이 발급되기때문에, 다른 방식보다 발급되는 Access Token의 유효시간이 짧은 편이다.
특별히 안전한 저장공간이 없는 Javascript SPA(Single Page Application)에 사용하기 위해 만들어졌지만 권장하지는 않는 유형이다.
3. Resource Owner Password Credentials Grant (자원 소유자 자격증명 승인 방식
직접적으로 유저의 아이디와 비밀번호를 받아서 클라이언트가 요청을 하는 방식이다.
직접적으로 유저 정보가 가는만큼, 이는 유저가 가지고 있는 기기의 os와 같은 굉장히 믿을 수 있는 그런 안전한 환경에서만 사용할 수 있다. 이 때문에 일부 특수한 경우에만 사용되며, 일반적으로 권장되지 않는다.
4. Client Credentials Grant (클라이언트 자격증명 승인 방식)
이 방식은 Client와 Resource Onwer가 같은 주체일 때, 복잡한 플로우를 가져가기보다 직접적으로 API 호출을 통해서 토큰을 발급을 받을 수 있는 방식이다.
이 방식은 자격증명을 안전하게 보관할 수 있는 클라이언트에서만 사용되어야 하며, Refresh Token은 사용할 수 없다.