로그인 유지 - 세션, JWT, OAuth 2.0

LTEN·2023년 1월 23일
0

BackEnd

목록 보기
1/6

지금까지는 서비스 만들면서 서버 사이드 렌더링 방식만 이용했고, REST API를 제대로 다뤄본 경험이 없었습니다.
따라서 REST API와 리액트 또는 뷰를 이용하는 프로젝트를 계획하고 하던 중 다음과 같은 의문이 떠올랐습니다.
'REST API는 stateless 하다면, 로그인은 어떻게 처리하지?'
(※추가: stateless 한 것은 HTTP 자체의 특성. REST API에서 세션을 사용하지 않는 것은 지향성)

정확히는 Restful한 서비스에서 로그인 상태를 어떻게 유지할 수 있는지 질문이 들었죠.

이 질문으로 시작해서 JWT, OAuth 등에 대해 접할 수 있어서, 각각에 대해 정리해두려 합니다.

1. 세션 방식

세션(Session)은 서버 측에서 사용자의 정보를 저장하는 방법입니다. 브라우저가 연결되면 해당 클라이언트에 대한 세션을 확보하고, 연결이 종료되면 세션을 닫게되죠.

세션을 이용하면 꽤 간단하게 로그인 정보를 유지할 수 있습니다.
1. 로그인 요청
2. 로그인 처리 -> 세션에 사용자 정보 저장
3. 로그아웃 -> 세션에서 사용자 정보 제거

이런 식으로 세션에 정보를 저장만 해두면, 필요할 때 확인만 하면 되죠.
예를 들어, 특정 사용자가 자신의 구매 목록을 보려하면, 세션을 통해 로그인한 사용자가 누구인지 확인한 후 해당 사용자의 구매 목록을 보여주면 됩니다.

세션은 사용자의 정보가 서버에서 보관되는 방식이기 때문에, 보안상으로도 안전하다고 볼 수 있습니다.

참고로 기본적으로 세션은 서버의 메모리에 사용자의 정보를 저장합니다. 다만 메모리의 단점을 극복하기 위해 하드 디스크나, DB까지 다양하게 사용한다고 합니다.

2. 토큰 방식, JWT

(JWT에 대해서는 얄코님 영상을 주로 참고했습니다. https://www.youtube.com/watch?v=1QiOXWEbqYQ)

세션 방식은 편리한 편이지만 질문이 하나 생깁니다.
REST API는 stateless하므로 사용자의 정보를 저장하지 않습니다. 즉, 세션을 이용하지 않는 것인데, 그렇다면 로그인을 어떻게 유지할 수 있을까요?

이에 대한 해결책이 토큰을 활용하는 방식 입니다. JWT(JSON Web Token)는 이때 사용되는 토큰입니다.
본격적으로 알아보기에 앞서 개인적으로 이 방식은 '티켓은 사용자에게 주고, 검수만 서버에서 하는 방식'이라고 설명할 수 있을 것 같습니다.
(세션은 서버에서 출입 장부를 기록하는 느낌이 되겠네요.)

토큰 방식은 다음과 같이 동작합니다.
1. 로그인 요청
2. 사용자에게 토큰 발급
이 토큰은 사용자의 정보를 담고 있습니다. 예를 들어, 'id=3인 사용자에게 발급한 토큰' 같은 느낌이죠.
자세한 구조는 뒤에서 살펴보겠습니다.
3. 사용자는 요청 시 토큰을 함께 전송
4. 서버는 토큰에 담긴 사용자 정보를 확인

그런데 딱 봐도 위 방식은 위험해보입니다.
위 예시에서 토큰을 수정하여 id=4인 사용자인 것처럼 속일 수 있을 것입니다.

당연히 실제로는 이러한 행위는 불가능합니다. 앞선 비유에서, 티켓은 사용자가 갖고, 서버는 검수만 한다고 했습니다.

티켓은 토큰일 것이고, 그렇다면 검수는 어떠한 것일까요?
JWT의 구조를 자세히 보겠습니다.

JWT 구조

JWT는 헤더.페이로드.시그니처 구조로 이뤄져있습니다.
ex) 2Efer6.TU222.019WW

가장 먼저 페이로드는 사용자의 정보가 인코딩된 부분입니다. 이 부분을 디코딩하면 아이디나 닉네임, 권한 등의 사용자 정보를 얻을 수 있죠.

이 부분을 조작하는 것이 위험한 것처럼 보입니다.

이때 필요한 부분이 헤더와 시그니처입니다.

헤더에는 암호화 알고리즘이 표시되어 있습니다.

여기서 검수의 의미를 알 수 있는데, 서버는 클라이언트의 정보를 저장하지는 않지만, 검수를 위해 비밀키를 보유하기는 합니다.

이 비밀키와 헤더, 페이로드를 조합하여 암호화한 결과를 시그니처와 비교했을 때, 일치하는지 확인하는 것이 검수 과정입니다.

이렇게 하면, 페이로드에서 사용자 정보를 수정하면 서버의 비밀키와 조합하여 암호화한 결과가 시그니처와 달라지겠죠?

이러한 원리로 JWT는 사용자가 조작할 수 없습니다.

보안, refresh token

그런데 여전히 한가지 문제가 남아있습니다.
이 JWT 자체가 해커에게 넘어가면 어떨까요?
서버는 티켓을 들고 온 사람이 발급 받은 사람인지, 티켓을 주운 사람인지 확인할 방법이 없습니다.

이를 해결하기 위한 방법이 refresh token을 이용한 방법입니다.
이때의 흐름은 다음과 같습니다.

  1. access token과 refresh token을 함께 발행
  2. access token의 만료기간을 짧게 설정
    -> access token은 넘어가더라도, 금방 만료됩니다.
  3. access token이 만료도었더라도, refresh token이 유효하다면 다시 access token 발행
    -> refresh token의 유효기간은 길게 설정합니다.

만약 refresh token까지 탈취당한 경우 어떻게 할까요?
이러한 문제 때문에 refresh token은 서버의 DB에도 함께 저장합니다. 만약 refresh token이 탈취된 것이 확인되면, 서버의 DB에서 해당 refresh token을 제거하면 되죠.

Refresh token의 유효기간과 별개로, DB를 통해서도 유효성을 한번 더 검사하는 것입니다.

(※추가 - refresh token 구현 방법은 다양합니다.)

한계

결국 Stateful

처음으로 돌아가보면, 토큰 방식은 stateless한 환경에서도 구축할 수 있다고 했습니다. 그러나 refresh token까지 오면서 결국 다시 DB에 저장할 정보가 생겼죠.
물론 stateful 한 환경이 나쁜 것은 절대 아닙니다. 그렇기 때문에 현재 refresh token까지 결합한 방식이 많이 사용되는 것이겠죠.
다만 statelss하지 않은 것은 초기 목표와 조금 벗어나므로 일종의 한계라고 볼 수 있겠습니다.

※ 첫 의문이였던 그럼 REST API에서는 어떡하지? 라는 질문때문에 찾아보니, 애초에 엄밀한 정의가 아닌 REST API는 완벽한 stateless 환경이 아닐 수 있다고 합니다.

3. JWT와 세션 비교

JWT는 session id에 비해 더 길고 복잡합니다. 그런데다가 refresh token을 사용하면 결국 stateful 해지는 것인데, 그렇다면 그냥 세션을 쓰면되지 않나? 하는 생각이 들었습니다.
결론부터 말하면, '정답은 없지만 JWT의 확실한 장점이 있습니다.'

서버 부하

많은 사용자가 몰리는 경우를 생각해보면 됩니다.
세션은 그 만큼의 세션을 관리해야하므로 부하가 크겠지만, 토큰 방식이라면 적어도 로그인 유지를 위해 서버에 추가되는 부담은 거의 없습니다.

분산 환경

서버가 분산된 경우, 예를 들어 로그인 서버와 게시물 기능을 담당하는 서버가 분리되어 있다면, 게시물을 작성하려할 떄 로그인 정보를 게시물 서버에서는 확인할 수 없습니다. (세션이 메모리에 올라가 있는 경우)
그렇지만 토큰 방식에서는 문제될 것이 없죠.

물론, 세션을 공유하는 것도 충분히 가능하다고 합니다.

그렇기 때문에, 토큰 방식이 더 우수한 것이 아닙니다.
결국 각각의 특징에 대해 이해하고, 운영하려는 서비스의 형태에 따라 적절하게 선택하는 것이 중요합니다.

4. OAuth 2.0

JWT에 대해 공부하다 보니 자연스럽게 OAuth에 대해 접할 수 있었습니다.
(OAuth 2.0에 대해서는 생활코딩님의 영상을 참고했습니다. https://www.youtube.com/watch?v=hm2r6LtUbk8)

OAuth 2.0 이란 한 서비스에서 다른 서비스에게 사용자에 대한 권한을 위임할 때 사용되는 표준 프로토콜입니다.

대표적으로 '네이버 아이디로 로그인, 카카오 아이디로 로그인' 등이 있겠네요.

OAuth 2.0과 JWT의 관계를 정리하면, OAuth 2.0 프로토콜의 산출물이 JWT 형태인 것입니다.

OAuth 2.0을 통해 권한이 위임되는 과정을 보겠습니다.
C라는 사용자가 A라는 서비스를 이용할 때, B 서비스를 통해 로그인하는 경우를 가정해보죠.
이런 경우 A를 client server, B를 resource server, C를 resource owner라고 표현합니다.

결국 OAuth 2.0 방식은 resource server가 clinet 서버에게 마치 client가 resource owner인 것처럼 토큰을 발행해주는 것입니다.

추가 - OAuth 2.0 소셜 로그인

OAuth 2.0을 처음 접했을 때 'OAuth 2.0은 client server가 resource server에 사용자 권한으로 접근할 수 있도록 하는 프로토콜인데, 사용자가 client 서버에 인증하는 것과는 무슨 상관이지?' 라는 의문이 들었습니다.
최근에 소셜 로그인을 직접 구현해보며 그 의문이 맞는 의문(?) 이었다는 것을 알 수 있었습니다.

OAuth 2.0 자체로는 client가 resource server에 사용자 권한으로 접근할 수 있는 프로토콜입니다.
여기에 추가로 소셜 로그인(+회원가입)을 구현하고 싶다면 다음과 같이 처리해야 합니다.
(1) resource server에 사용자 정보 요청
(2) 사용자 정보를 바탕으로 별도의 회원가입 및 로그인 처리

(1)은 OAuth 2.0 프로토콜로 할 수 있는 것이고, (2)는 추가로 구현해야 하는 것이 맞습니다.


잘못된 내용이 있다면 댓글로 알려주세요!
profile
백엔드

0개의 댓글