[JWT] Session과 JWT에 대해 알아보기

박진형·2021년 12월 16일
1

JWT

목록 보기
1/4

이전까지의 프로젝트에서는 Session을 이용해서 인증을 했다. 이번에 새로운 프로젝트를 진행하면서 Spring Security를 사용하게 되었고, 전부터 눈여겨 보았던 JWT를 사용해보기로 했다.

JWT와 Session

JWT 방식과 session 방식의 차이점을 알아보자.

Session

사용자 로그인 시 로그인 정보를 서버의 세션 저장소에 저장한다. 그리고 저장한 로그인 정보를 찾을 수 있는 key인 세션ID를 쿠키에 저장해서 클라이언트로 보낸다.

이렇게 사용자는 쿠키에 저장된 세션ID를 요청마다 서버로 보내 서버에 저장되어있는 로그인 정보를 찾는 방식으로 인증을 한다.

Session 방식의 장단점

  • 장점
    • 쿠키에 저장된 세션ID 하나만으로는 유의미한 정보를 가지고 있지않다. 중요한 정보는 서버에 저장되어있기 때문에 쿠키를 탈취당해도 비교적 안전하다.
  • 단점
    • 서버에 세션 정보를 저장하기 때문에 부하가 증가한다.
    • 세션 하이재킹 공격이 가능하다.

JWT

JWT(Json Web Token)은 인증에 필요한 정보를 암호화시킨 토큰을 사용하는 방식이다. 세션 방식과 유사하게 JWT 토큰을 HTTP 헤더에 실어 서버가 클라이언트를 식별한다.

JWT 구조

JWT는 '.'을 구분자로 아래와 같이 세 부분으로 나누어져 있다.

  • 헤더(Header)
    • typ : 토큰 타입 지정
    • alg : 해싱 알고리즘 지정
  • 내용(payload)
    • 토큰에 담은 정보를 저장
    • 클레임(Claim) : 담긴 정보의 단위. registered, public, private 클레임으로 나뉜다.
  • 서명 (signature)
    • 헤더의 인코딩 값과 정보의 인코딩 값을 합친 후 주어진 비밀키로 해쉬하여 생성
    • 헤더(Header)와 내용(payload)는 단순히 인코딩된 값이기 때문에 제 3자가 복호화할 수 있지만 signature 값은 서버측에서 관리하는 비밀키 값이 유출되지 않는 이상 복호화 할 수가 없다. 따라서 signature는 토큰의 위변조 여부를 확인하는데 사용된다.

JWT 구조에 대해서는 자세히 알 필요가 없을것 같다. JWT관련 라이브러리가 다 알아서 처리해주기 때문에 로직만 잘 짜면 될 것 같았다.

JWT 인증 절차

  1. 클라이언트에서 로그인 요청이 들어오면 클라이언트의 고유 ID 등을 payload에 담고, 비밀키를 사용하여 AccessToken(JWT)를 발급하여 클라이언트로 전달한다.

  2. 클라이언트는 전달받은 토큰을 안전한 저장소(나중에 언급하게 됨)에 저장해둔다.

  3. 클라이언트가 서버에 요청을 할 때마다 토큰을 전달한다.

  4. 서버는 전달받은 토큰의 위변조 여부, 유효한 토큰인지 판단을 하고 유효하다면 요청에 응답한다.

JWT 방식의 장단점

  • 장점
    • signature로 데이터의 위변조를 막을 수 있다.
    • session 방식과는 다르게 무상태(stateless)한 서버가 된다.
    • 확장성이 우수하다.
      • 토큰을 기반으로하는 인증 시스템(Facebook, Google, Microsoft 등)에 접근이 가능하다.
      • 세션은 서버에 저장되기 때문에 많은 요청이 몰린다면 서버에 과부하가 갈 수 있고, 그 과부하를 해결하기 위해서는 서버를 늘려야 된다. 서버가 여러대면 세션을 처리하기 복잡해진다. (여러 서버중 Session 정보가 없는 서버에 요청이 간다면 다시 로그인 해야하는 불편함)
  • 단점
    • payload 자체는 암호화 되지않기 때문에 payload에 포함할 수 있는 데이터가 한정적이다.
    • 토큰의 길이가 길어지면 네트워크 부하가 심해질 수 있다.
    • 토큰을 탈취 당하면 대처가 어렵다.
      • 토큰은 한번 발급되면 유효기간이 만료될 때 까지 계속 사용이 가능하다.
      • session 방식은 서버에서 세션만 삭제하면 되므로 비교적 관리하기 편하다.
      • 이러한 문제를 해결하기위한 몇가지 보안 전략이 있다.

JWT의 보안 전략

짧은 만료 기한 설정

토큰의 만료 기한을 짧게 설정한다면 토큰이 탈취되더라도 피해를 줄일 수 있다.
하지만 만료될때마다 매번 다시 로그인을 해줘야하는 엄청난 불편함이 발생한다.

RefreshToken 사용

토큰의 만료 기한을 짧게 설정한 탓에 로그인을 해줘야하는 불편함을 해결할 수 있는 방법이다.
이 방식은 AccessToken의 만료 기한을 짧게두고, RefreshToken의 만료기한을 길게 두어, AccessToken이 만료되었다면 이에 대응되는 RefreshToken을 통해 새로운 AccessToken을 발급해주는 방식이다.

이 방식은 RefreshToken이 서버 측의 별도의 저장소(redis 사용)에 저장되어야한다. 이렇게되면 JWT 방식이 갖는 빠른 인증처리라는 장점이 흐려지게된다.
하지만 RefreshToken에 대한 만료를 강제할 수 있고 추가적인 AccessToken의 재발급을 제어할 수 있다.

토큰은 어디에 저장해야 안전할까?

토큰의 저장은 세션 스토리지, 로컬 스토리지, 쿠키 등을 고려해볼 수 있다.
이러한 방식들은 XSS 공격, CSRF 공격 등에 노출될 수 있다. 그렇다면 각 방식은 어떤 장단점을 가지고 있을까?

  • 로컬 스토리지에 저장
    • CSRF 공격에 안전하다.
    • 자바스크립트로 WebStorage에 저장하게 되는데 이는 XSS 공격에 취약할 수 있다.
  • 세션 스토리지에 저장
    • CSRF 공격에 안전하다.
    • 로컬 스토리지 방식과 마찬가지로 WebStorage에 저장되기 때문에 XSS 공격에 취약할 수 있다.
    • 브라우저를 끄게되면 데이터가 삭제되므로 로그인을 다시해야하는 불편함이 생긴다.
  • 쿠키에 저장
    • XSS 공격에 안전하다. 쿠키의 옵션인 HttpOnly, Secure옵션을 사용하면 XSS 공격을 막을 수 있다.
      • HttpOnly : 이 옵션을 사용하면 자바스크립트로의 접근이 불가능하고 Http통신으로만 쿠키가 저장된다. (XSS 공격에 방어가 가능하다.)
      • Secure : HTTPS 통신으로만 전송되기 때문에 보안 수준이 향상된다.
    • CSRF 공격에도 대비가 가능하다. 쿠키에 samesite 속성과 lax속성을 사용한다면 CSRF 공격에 대비할 수 있다고 한다.

다양한 블로그의 글을 읽어 봤는데 어떤 방식이 최선이라고 단언할 수 없을만큼 각각 선호하는 방식이 달랐다. 하지만 이러한 장단점을 미루어 보았을때 나는 Cookie방식이 조금 더 안전하다고 생각해 Cookie 방식을 채택하기로 했다.

마무리

이렇게 기존에 사용하던 Session 방식과 JWT는 어떤 차이가 있는지 알아보았다.
다음 포스팅에서는 실제 프로젝트에서 어떻게 JWT를 적용했는지, 앞서 언급한 RefreshToken이 어떻게 사용되는지에 대해서 자세하게 작성할 예정이다.
사실 JWT를 구현하는 사람마다 각기 다른 방식으로 구현하고 어떤게 옳은 방법인지 명확하지 않아 이 글의 내용이 정석이라고 말 할 수는 없다. JWT를 공부하고 구현하면서 수십개의 글을 읽고도 명확한 답이 나오지 않았었고 나름 최선이라고 생각되는 방식으로 구현해나갔다. 나와 같은 사람이 많지않을까 하는 생각으로 내가 어떤 고민을 했고 어떤 근거로 선택을 했는지 써내려갈 것이고 많은 이에게 도움이 되었으면하는 바램이다.

0개의 댓글