Session과 JWT Token을 통한 로그인 방식

이정규·2022년 5월 26일
0

HTTP통신

우리가 알고 있는 HTTP통신은 stateless하다. 이 뜻은 서버는 응답만하는 기계일 뿐이지, 요청자가 누구인지 기억하지 않는다는 뜻이다.

API서버를 만들다 보면 개인정보에 관한 내용들은 누구에게 함부로 응답을 줘서는 안된다. 인증된 클라이언트에게만 정보를 전달해야한다.
그런데 서버는 클라이언트가 누구인지 모른다고 했다. 그렇다면 클라이언트가 서버에게 나는 누구야! 라고 알려줘야 할텐데 어떻게 진행하면 좋을까?

인증 과정

  1. 클라이언트가 서버에게 로그인 요청을 보낸다.
  2. 로그인이 성공적으로 이루어지면, 서버는 인증 정보를 클라이언트에게 전달한다.
  3. 앞으로 클라이언트는 요청할 때, 서버에게받은 인증 정보를 같이 보내 자신이 인증된 사용자라는 것을 서버에게 알린다.

그런데 여기서 서버는 "인증된 사용자" 라는 것을 어떤 방식으로 처리할까?

1. Session 로그인

세션 로그인이란 서버에서 {"session ID" : "value"} 값을 생성하여 메모리상에 저장해두고, session ID 값을 쿠키로 만들어 클라이언트에게 전달한다.

그런 뒤에 클라이언트가 session ID값을 쿠키로 전달해주면 해당 session ID값에 해당하는 value를 조회해 해당 유저의 정보를 알려주게 된다.

장점

  1. 서버에서 클라이언트 상태 정보를 갖고 있으므로, 사용자의 로그인 확인 여부나 강제 로그아웃등을 할 수 있다.

단점

  1. 서버의 메모리를 잡아먹는다.
    => 수많은 이용자가 로그인을 하면 그 때마다 서버에서 {"session ID" : "value"} 값을 생성하여 관리하게 된다.
  2. 서버를 확장시키기가 어렵다.
    => 여러 대의 서버를 운영한다면, 각 서버마다 세션 정보들을 갖고 있어야 한다.
    이는, in-memory-DB등을 운용하여 해결할 수 있다.

2. JWT Token 로그인

세션 로그인은 "서버가 클라이언트 상태 정보"를 갖고 관리 및 인증을 하는 반면 JWT Token은 신경쓰지 않는다.
처음 JWT Token을 발급한 뒤부터는 오로지 해당 토큰이 유효한지 유효하지 않은지만 판단하여 인증을 진행한다.

그렇기 때문에 서버에서는 발급하는 비용, 유효한지 판단하는 비용만 존재한다.
서버에서 어떤 정보를 저장할 필요가 없다.

JWT Token의 구성

토큰의 타입과 해싱 알고리즘의 종류를 저장한다.
base64url로 인코딩된다.

JWT Token은 base64가 아닌 base64url이라는 것으로 인코딩된다고 한다.
이유를 찾아보니 예전에 url로 jwt가 전달되었었는데 이 부분이 표준이 되서 지금까지도 쓰이는 것 같다.
https://stackoverflow.com/questions/56711129/why-do-you-use-base64-url-encoding-with-json-web-tokens

Payload

토큰에 담을 정보가 들어간다.
무엇이든 들어갈 수 있지만 암호화되어있는 것이 아니여서 중요한 정보는 담으면 안된다.

일반적으로 토큰의 발급시간, 발급자, 사용자 아이디값등이 들어간다.
base64url로 인코딩한다.

Signature

이 부분이 JWT Token이 유효한지 아닌지 판단할 수 있는 부분이다.
Header + Payload를 합친뒤, 서버에서 설정한 Secret Key를 이용하여 Header에 나타난 해싱 알고리즘으로 암호화를 한다.
그 다음 base64url로 인코딩한다.

인증하는 부분은 Signature을 복호화하여 넘어온 Header, Payload와 값이 일치하는지 아닌지를 판단하여 인증을 진행한다.

그렇게 {Header}.{Payload}.{Signature} 형식으로 JWT Token이 완성된다.

장점

  1. 클라이언트에 JWT Token을 관리하므로 서버에 부담이 없다.
  2. 서버는 토큰을 생성하여 전달만 하면 되므로 서버 확장에 용이하다.

단점

  1. 서버에 클라이언트에 대한 정보가 전혀 없으므로 토큰을 통해서만 클라이언트를 전적으로 믿을 수 밖에 없다.
  2. 사용자의 로그인 여부 확인이나 강제 로그아웃등의 제제를 가할 수 없다.
  3. JWT Token은 Session보다 길이가 길다. 이를 요청마다 보내게 되면 HTTP Request의 오버헤드가 커진다.

클라이언트의 저장 위치

클라이언트는 Session이나 JWT Token 어느것이든 이용한다고 한들, 인증 정보를 갖고 있어야 한다.
이 인증 정보들을 어디에다가 저장해놔야 안전할까?

진짜 이 부분은 며칠동안 고민하고 찾아봤다. 하지만 보안이라는 것이 100% 완전 보안! 이라는 말은 존재하지 않는다. 최대한 막아보자. 라는 것이 우리의 목표일 뿐이다.
그래서 사람들마다 생각이 다르다. 이 부분도 결국 나의 생각일 뿐, 이걸로 해야한다! 라는 뜻은 아니다.

LocalStoarge

  1. 최대 5MB까지 저장가능한 저장소이다.
  2. 영구적으로 저장이 가능하다.
  3. HTTP통신이면 무조건적으로 전달되는 쿠키와는 달리 선택적으로 HTTP통신에 넣어 전달할 수 있다.
    => CSRF공격에 대응할 수 있다.

단점

  1. XSS공격에 취약하다. js코드로 접근이 가능하기 때문이다.
  1. 최대 4KB까지 저장가능한 저장소이다.
  2. HttpOnly 옵션을 통해 XSS공격에 대응이 가능하다.
  3. 다양한 옵션이 존재한다. httponly, secure, samesite등..

단점

  1. CSRF공격에 취약하다.
    => 이는 옵션을 통해 어느정도 대응할 수 있다.

결론

LocalStorage는 너무 쉽게 접근이 가능하다. XSS공격도 쉽게 당할 수 있다.
그래서 보안적으로 유리해보이지 않는다.

즉, Cookie를 사용하는 것이 옳다는 의견이다.
HttpOnly옵션을 주면 브라우저에서 코드상으로 접근이 불가능하게 막을 수 있다.
Secure옵션을 주면 Https통신에서만 쿠키가 전달되도록 하여 보안을 높일 수 있다.
SameSite 옵션을 통해 같은 도메인상에서만 api호출이 가능하도록 만들 수 있다.

마지막으로 세션과 JWT중 어떤 것이 좋은가요?

이 부분도 궁금했다. 위에서 말했다싶이 세션과 JWT는 서버에서 클라이언트 정보를 관리하냐, 안하냐의 차이이다.

세션이든 JWT Token이든 클라이언트 상에 정보를 저장한다는 것은 동일하다.
그러므로, 탈취당할 위험성도 동일하다.

하지만, 세션은 클라이언트가 "나 해킹당했어!" 라고 서버에게 말해주면 서버는 해당 세션 id값을 파기하고 새로 발급해주는 식으로 어느정도 대응이 가능하다.
JWT는 "나 해킹당했어!" 해도 어떻게 대응할 방도가 존재하지 않는다. Signature의 해싱방식을 바꾸든, secret key를 바꿔서 해결 할 수 밖에 없다.
그렇게 되면 모든 사용자가 다시 인증을 받아야 하니 좋은 대응 방안은 아닌 것 같다.

세션과 JWT의 차이는 메모리를 잡아먹느냐, 개개인의 사용자를 제어할 수 있느냐. 의 차이인 것 같다.

결론은 현재 프로젝트에 어떤 성향이 더 맞는지를 판단하여 적용하는 것이 옳다. 더 좋다 라는 개념은 없다.

참고
https://millo-l.github.io/Session-기반-인증방식
https://github.com/boojongmin/memo/issues/7
https://ledgku.tistory.com/72
https://velopert.com/2389
https://velog.io/@0307kwon/JWT는-어디에-저장해야할까-localStorage-vs-cookie

profile
강한 백엔드 개발자가 되기 위한 여정

0개의 댓글