OAuth와 JWT는 팀프로젝트를 하면서 로그인 관련 기능을 구현할 때에 처음 제대로 알게 되었습니다.
OAuth는 리소스 서버가 클라이언트(이 클라이언트는 리소스 서버 입장에서 클라이언트를 의미합니다)에게서 인가 코드를 받고 액세스 토큰(Access Token)을 넘겨주기 때문에 프론트나 백에서는 직접 구현할 사항이 없고, JWT 또한 백엔드에서 사용하지 프론트에서는 백엔드로 로그인 request를 보냈을 때 response의 headers에 담아서 보내주는 액세스 토큰과 리프레시 토큰을 저장하는 게 끝입니다. 물론 보안 강화를 위해 TTL을 짧게 설정해두므로 리프레시 토큰으로 액세스 토큰을 재발급 받는 처리도 해줍니다. 프론트가 JWT 토큰 발급을 직접 구현을 하는 것은 아니지만, 어떤 서비스를 구현하든 이메일 로그인이 필요하므로 이것을 가능하게 해주는 OAuth와 JWT가 어떤 일을 하는지 알아야 할 필요가 있다고 생각합니다.
먼저 OAuth는 웹 서버에 사용자 계정의 비밀번호를 제공하지 않고 SNS 계정(=리소스 서버)의 정보 중 일부에 대해 접근 권한을 부여할 수 있습니다. 즉, 웹 서비스를 이용할 때 사용자로 하여금 구글이나 카카오 계정으로 로그인 할 수 있도록 만들 수 있습니다.
만약 OAuth가 제공하는 방식으로 사용자 정보를 받지 않는다면 보안상으로 아주 좋지 않은 과정을 거쳐야 합니다.
위와 같은 방식은 안전하지 않은 인증 방식입니다. 사용자가 서비스에 자신의 구글 계정 정보를 다 알려주고 서비스가 구글에 로그인을 해본 뒤 사용자 정보를 얻어서 정상적으로 로그인이 되는 걸 확인한 뒤 정보를 얻으면, 사용자에게 로그인 성공 응답을 주는 거죠. 이는 사용자의 개인 정보를 서비스에 주는 것이므로 사용자는 이런 서비스를 사용하지 말아야 합니다.
그래서 직접적으로 서비스에 정보를 모두 제공하면서 사용자 입장에서 매우 불안한 과정을 거치지 않고, 안전하게 이용할 수 있도록 토큰을 발급하여 토큰을 주고 받는 형식으로 사용자 정보를 제공받을 수 있는 방법이 있는데, 바로 액세스 토큰(Access Token)입니다.
클라이언트(서비스)가 리소스 서버에 사용자의 액세스 토큰을 전달하면 리소스 서버가 해당 사용자의 정보를 클라이언트에게 제공합니다. 즉, 사용자가 우리가 구현한 서비스에서 구글 로그인을 하게 되면 구글 서버에 액세스 토큰을 전달하고 구글은 사용자의 일부 정보를 서비스에게 줍니다. 그래서 사용자 입장에서 서비스에 내 계정의 보안과 관련된 정보를 노출하지 않아도 되기 때문에 사용에 부담이 없어집니다.
현재 각 리소스 서버의 계정을 사용한 로그인 기능을 이용하면, 거의 OAuth 2.0을 사용하고 있습니다.
2.0은 버전을 의미할 뿐 위에서 설명한 OAuth의 동작 방식이나 구성 요소에서 차이가 없습니다.
OAuth 기반의 액세스 토큰 교환을 통한 로그인 과정을 시퀀스 다이어그램으로 이해하려면 아래 그림을 참고하세요.
JWT는 JSON, 쿠키, 세션에 대한 기본 지식이 조금 필요합니다.
이는 다른 포스트에서 다룰 것이기 때문에 후에 참고해도 좋을 것 같습니다.
JWT는 OAuth와 마찬가지로 토큰을 발급하고 교환하는 방식으로 사용자 정보를 얻습니다. 서비스의 사용자가 로그인 페이지에서 필수 정보를 입력하고 로그인 요청(request)을 보내면 DB에 저장된 사용자의 아이디 비밀번호가 일치할 때 서버에서 토큰을 생성하고 유저에게 토큰을 발급합니다.브라우저에서는 이를 저장하여 액세스 토큰을 요구하는 request를 보낼 때 header에 같이 담아서 액세스 토큰을 보내고, 서버에서는 토큰을 확인하면 이를 검증한 후에 response를 보내고 API가 동작합니다.
JWT는 세 가지 구성요소를 가집니다.
xxxx[Header].yyyy[Payload].zzzz[Signature]
ex)
| 구성 요소 | 설명 |
|---|---|
| Header | {”alg” : “HS256”, “typ” : “JWT”} |
| Payload | {”sub” : “user”, “id” : “admin”} |
| Signature(서명) | 위 두 내용에 대하여 적절한 서버 키 값을 더해 해싱한 값 |
유저는 자신이 받았던 토큰을 서버에 전달합니다. 서버는 헤더 + 페이로드 + 서버 내에 있는 키를 해싱한 값(해시 값)이 사용자로부터 전달받은 것과 일치하는지 체크합니다. 이 과정에서 서버가 가지고 있는 비밀키를 사용합니다.
JWT를 사용하면 사용자는 서버가 처음에 부여했던 권한만큼의 작업을 요청할 수 있습니다.
이 때 데이터를 변경하면 해시 값도 변경되므로, 악의적인 공격자가 페이로드를 수정하는 것이 불가능하죠.
ex)
사용자 등급이 1, 2, 3, 4, 5일 때 5등급의 사람이 1등급으로 페이로드를 변경하여 서버에 보냄
→ 하지만 5등급의 사람은 서버의 비밀키를 모르므로, 서명 값이 일치하지 않아서 서버가 위조 여부를 알 수 있음.
장단점 같은 경우에는 세션에 대해서도 알아야 하지만, 세션과 반대되는 성질을 가지고 있기 때문에 아래의 내용을 확인하면 세션에 대해서 먼저 모르더라도 세션이 어떤 특징을 가지는지 대략적으로 알 수는 있습니다.
장점
단점
단점에서 언급됐듯 페이로드는 중간자 공격에 의해 노출될 수 있으므로 페이로드에는 가능한 민감 정보를 넣지 않아야 합니다. 애초에 JWT의 목적은 본래 정보 보호보다는 다음의 목적에 가깝습니다.
이렇게 OAuth와 액세스 토큰, 그리고 JWT에 대해 알아봤습니다.