쿠키, 세션 그리고 JWT

드엔트론프·2023년 7월 2일
1
post-thumbnail

들어가며

최근 스터디에서 JWT를 주제로 발표할 기회가 있었다. 이번 기회에 제대로 설명하기 위해 준비했다가, JWT를 더 잘 이해하기 위해선 쿠키나 세션 방식을 알아야 이해가 더 쉬웠다. 그래서 순서대로 정리하며 JWT를 설명하려한다.

JWT

JWT란 인가(Authorization)에 필요한 정보들을 암호화시킨 JSON 형태의 토큰을 의미한다.

  • 그렇구나, 하고 이 한줄을 넘어가려 했지만 눈에 들어온 단어는 인가와 토큰이었다.
  • 그렇다면 인가를 먼저 알아보자. 인가가 뭔데 ?

인증과 인가

  • 인증(Authnetication)은 간단히 말해 로그인을 하는 것이다. 나라는 사람이라는걸 검증하기 위한 것.
  • 인가(Authorzation)은 인증 이후, '나'라는 사람이 글쓰고 댓글달고 할 때 '나'임을 인식하고 허가 해주는 행위이다.

아 이러한 인증과 인가를 처리하기 위해 JWT를 쓰는구나 ~ 근데 JWT로 쓰면 끝인건가?
아쉽게도 아니다. 인증과 인가를 처리하는 JWT 토큰 이전의 방식이 쿠키, 세션 방식이다.
그렇다면, JWT라는 토큰방식이 나오기 이전의 방식들에 단점이나 문제가 있었나보다. 어떤건지 알아보자.

쿠키

  • 쿠키란, 서버에서 사용자 브라우저로 전송하는 작은 데이터다.
  • HTTP 프로토콜의 큰 특징 중 비연결성, 무상태성이 있다.
  • 로그인을 하고나면 나라고 확인해야 될 때, 그때마다 계속 확인해야한다면 그것만큼 번거로운 일이 없을거다. 이러한 상태를 저장하기 위해 쿠키라는 개념이 생겼다.

간단한 로직을 살펴보자.

  • 내가 Kang이라는 user로 로그인 요청을 보내면 서버에서 확인 후 문제가 없을 시 필요한 정보를 쿠키에 담아 보내주게 된다. 이 데이터를 브라우저의 쿠키에 저장하게 된다.

  • 나라는 사람임을 확인하는 상황이 있을 때면, 쿠키에서 조회한 값을 HTTP요청 할 때 쿠키에 담아 보낸다. 문제가 없다면 서버는 문제가 없다는 응답을 해주고 나는 다음 행동을 할 수 있게 된다.

  • 그러나 이러한 쿠키의 큰 단점이 있다. 바로 브라우저에 직접적으로 정보 값을 저장하기에 유실,변조,도난되기 쉽다는 것이다.

이러한 쿠키의 단점을 보완하기 위해 세션 방식이 나오게 됐다.

세션 방식

  • 세션 방식은 기존의 쿠키를 활용하되, 쿠키에 직접적인 값을 넣지 않고 식별값인 sessionID를 쿠키로 주고받게 된다. 중요한 정보는 서버에 저장하기에 쿠키만 사용할 때보단 훨씬 안전하다.

쿠키와 비슷하지만 간단한 예시를 또 살펴보자.

  1. User Kang으로 로그인 요청을 보내면
  2. 서버는 확인 후 해당 유저에 맞는 SessionId를 생성한다.
  3. 그리고 SessionID를 쿠키에 전달하고,
  4. 나는 이 SessionId를 브라우저 쿠키에 저장한다.

  1. 또 나라는 사람이 확인되어야 하는 상황이라면, 쿠키에서 SessionID를 서버 요청시에 함께 전달한다.
  2. 서버는 해당 SessionID값을 조회하고
  3. 문제가 없다면 다음 행동을 진행하게 된다.

  • 앞서 말했듯, 세션 방식은 쿠키만을 사용한 방식보다 안전하다.

  • 하지만 당연하게도 완벽한 기술은 없다. 세션 방식은 다음과 같은 단점이 있다.

    1. 메모리 사용에 대한 리스크
    - 일단 메모리라는 곳은 에러가 나게 되면 다 날아가게 되는 큰 위험이 있다. 그렇기에 하드디스크나 데이터베이스에 담기도 하지만, 하드디스크나 데이터베이스는 메모리보다 느리다.
    2. 다중 서버일 때 세션 구분
    - 만약 서비스가 커서 서버가 여러대라면 ? 내가 로그인했을 때 내 SessionId가 A에 저장됐는데, 조회할 때는 B에서 조회하면 ? 당연하게도 문제가 된다. 이런걸 해결하기 위해 공용 메모리형 데이터베이스를 사용한다고 한다.

    이렇게 세션 방식처럼 구현하려면 하겠지만 구현하기도 쉽지 않은 일. 좀 더 쉽게 할 수 없을까? 해서 나온게 토큰 방식이다.

    토큰방식


  • 토큰방식은 세션 방식처럼 서버에 SessionId를 저장하지 않으면서도 토큰이라는 것을 통해 나를 인증/인가 해주는 방식이다.

  • 토큰 방식은 크게 세 가지의 특징이 있다.
    1. 상태유지를 하지 않는다. - 상태 유지를 하지 않는데 어떻게 상태를 알고 있냐 ? 토큰에 필요한 정보들이 들어있기에 된다.
    2. 확장성 있다. - 세션처럼 따로 저장하는 게 없으니, 확장에 유리하다.
    3. 확장성 있다22 - 네이버로 로그인, 카카오톡으로 로그인 같은 것들도 모두 토큰을 활용한 기능이고, 이때 선택적인 권한을 부여해서 발급하는 것, 로그인의 기능이 확장된다고 볼 수 있다.

  • JWT는 이러한 토큰 방식중의 하나이다.


JWT

앞에서 본 JWT를 다시 보자. JWT란, 로그인 이후에 발생하는 '인가'에 필요한 정보들을 암호화시킨 JSON형태의 토큰이다.

  • 좀 더 와닿나? 그러면 본래의 주제인 JWT에 대해 좀 더 알아보자.

  • 크게 세 부분으로 구분된다. 각 부분은 ' . ' 으로 구분되며 헤더, 페이로드, 서명으로 구분된다.

페이로드 (Payload)

  • 페이로드를 먼저 보자. 페이로드는 내가 원하는 정보를 담고 있는 부분이다.
  • 토큰의 유효기간, 발급처, 그리고 내가 필요한 정보들 (가령 닉네임, 관리자, 사용자 구분 여부 등)을 담는 부분이다.
  • 이렇게 토큰에 담긴 사용자 정보 등의 데이터를 Claim이라고 한다.
    • 사용자가 받아서 갖고있는 토큰 자체에 이런 정보들이 들어있으면 서버가 요청마다 일일이 데이터베이스에서 뒤져봐야할 것들이 준다.
    • 그런데 이 페이로드는 Base64라는 방식으로 인코딩 되어있는데, 이건 디코딩하게 되면 안에 내용을 바로 알 수 있다. 그러면 디코딩해서 조작이 너무 쉬운거 아닌가 ?!
    • 그걸 방지하기 위해 헤더와 서명값이 있는 것이다.

헤더 (Header)

  • 헤더 안에는 두 가지가 들어간다.
  1. type => JWT
    • JWT를 고정으로 넣어준다.
  2. alg
    • 어떤 알고리즘을 통해 암호화할지 정한다. 주로 HS256이라는 알고리즘으로 암호화를 한다.

서명 (Verify Signature)

  • 헤더, 페이로드 그리고 ‘서버에 감춰놓은 비밀 값’ 이 셋을 이 암호화 알고리즘에 넣고 돌리면 3번 서명값이 나온다.
  • 이 암호화 알고리즘은 한쪽 방향으로는 계산이 돼도 반대쪽으로는 안되는거라, 서버만 알고있는 그 비밀 값을 찾아낼 방법이 거의 없다고 보면 된다. 글자 하나만 바뀌어도 3번 서명값이 완전 달라진다.
  • 서버는 요청에 토큰 값이 실려들어오면 1,2번의 값을 ‘서버의 비밀 키’와 함께 돌려봐서 계산된 결과값이 3번 서명 값과 일치하는 결과가 나오는지 확인한다.
  • 만약 2번 페이로드의 정보가 서버가 아닌 누군가에 의해 조금이라도 수정되었다면 당연 맞지 않게된다.
  • 서명값과 계산값이 일치하고, 유효기간도 지나지 않았다면 → 그 사용자는 로그인 된 회원으로서 인가를 받는 것이다.
  • 그렇기에 서버는 사용자 정보를 저장할 필요없이 요청 들어올 때마다 토큰을 스캔해서 사용자를 걸러낸다.

아주 간단하게 보자면, JWT는 위와 같은 방식이다. 크게 보자면 쿠키 방식과 크게 다를 게 없어 보인다.


JWT 장점

  • JWT는 당연하게도 토큰의 특징이 장점이다. 더불어 데이터 위변조 방지가 추가된 장점이라고 볼 수있다.

  • 다만 아쉽게도 JWT가 만능은 아니다. 위와 같은 단점이 있다.
  • JWT는 한번 만들면 서버에서 제어가 안된다.
  • 이 토큰이 만약 탈취당한다면 ? 어떻게 무효화할까 ?
    • 이걸 조금이라도 막기 위해 토큰의 유효기간을 짧게 가져간다.
    • 유효기간을 짧게가져가면 매번 다시 로그인을 해야하나?
    • 그걸 방지하기 위해 유효기간이 짧은 토큰과 조금 긴 토큰 두개를 발급하는 방법이 있다.

토큰 탈취를 예방하기 위한 Access Token, Refresh Token

  • 유효기간이 짧은 토큰을 Access Token , 조금 긴 토큰을 Refresh Token 이라고 부르며, 보통 긴 토큰은 유효기간을 2주 정도로 준다.

  • 구현 방법은 다양한데,

  1. 리프레시토큰 상응값을 데이터베이스에 저장.
  2. 로그인 한 유저는 엑세스토큰 수명 다하면 리프레시 토큰을 보낸다. →
  3. 서버는 데이터베이스에 저장된 값과 대조, 맞으면 새 엑세스 토큰 발급해줌

  • 이렇게 리프레시 토큰만 안전하게 관리되면 이게 유효 할 동안은 엑세스토큰이 만료될때마다 다시 로그인 할 필요없이 새로 발급 가능하다.

    • 그러면 누가 토큰 탈취해도 → 탈취된 토큰 리프레쉬토큰을 데이터베이스에서 지워버려서 갱신 안되게 해버린다.
  • 근데 리프레시 토큰을 데이터베이스에 저장하면 세션 방식과 뭐가 다른가?

    세션은 사용자가 로그인 한 경우, 사용자 정보가 서버 메모리에 계속 상주해 있어 매번 crud 할때마다 서버 메모리에 접근해서 인증을 하지만,
    토큰은 서버 메모리에 저장 없이 넘어오는 토큰으로 인증만 진행 하기 때문에 가볍다. 그리고 refresh token은 access token 기간이 다 지났을 때만 갱신, 조회 하므로 db에도 큰 무리가 없다.


  • 다만 엑세스토큰의 유효기간이 끝나기 전까지는 방지하기 어렵다는 JWT의 한계가 있다.

요약

  • 서비스에 따라 세션 방식, 토큰 방식을 사용하는 기준이 나뉜다. 어떤 것이 맞다고 할 수 없다.

마치며

  • 조사하다보니 JWT 암호화 방식도 찾아보게 되고, 곁가지로 알아보게 되는 개념들이 많아졌다.

  • 추가적으로, 이러한 정보를 저장하는 쿠키나 웹 스토리지의 내용도 필요함을 느꼈다.

  • 틀린 부분을 보신다면 꼭 알려주시면 감사하겠습니다

출처

profile
왜? 를 깊게 고민하고 해결하는 사람이 되고 싶은 개발자

0개의 댓글