본문 바로가기
Spring

JWT란 무엇인가?

by wwns 2022. 11. 18.
반응형

이번에는 인증에 많이 사용되는 Jwt 토큰에 대해서 정리해본다.

 

JWT

  • Json Web Token의 줄임말로 RFC 7519에 명세되어 있는 국제 표준
  • 통신 양자간의 정보를 JSON 형식으로 사용하여 안전하게 전송하기 위한 방법
  • Jwt는 정보가 토큰 자체에 포함된(Self-Contained) 클레임(Claim) 기반 토큰이다
  • Jwt는 인증(Authentication)과 인가(Authoriation)에 사용되는 것이 가장 일반적
    • 인증 절차를 거쳐 서버에서 jwt 토큰을 발급해주면 클라이언트는 이를 잘 보관하다가 API 호출 시 서버에 jwt 토큰을 함께 제출하여 서버로부터 행위에 대해 인가받을 수 있다.
  • 해시 혹은 비대칭키 방식을 사용하여 서명(Signature) 하기 때문에 무결성을 검증할 수 있다
  • URL에 대해 안전한 문자열로 구성되어 있어 어떤 경로로든 전송할 수 있다
Jwt 토큰을 통해 사용자를 인증하고 권한을 인가한다는 것은 알겠다. 해시 혹은 비대칭키 방식을 통해 암호화하는 것도 알겠다 인증받은 사용자의 토큰을 탈취당하면 인가받은 행위들에 대해 탈취당하는 것이 아닌가라고 생각할 수 있다. 맞는 말이다. 어떻게 그런 취약점을 보완하는지도 알아보도록 하겠다!

 

서버 기반 인증 VS 토큰 기반 인증

  • 서버 기반 인증
    • 사용자가 성공적으로 로그인한 이후 서버에서 사용자에 대한 세션(Session)을 생성
    • 사용자의 브라우저는 세션 ID를 저장하는 쿠키가 생성됨
    • 서버는 세션 ID를 통해 사용자를 식별하고, 사용자에 대한 정보를 저장, 관리
  • 서버 기반의 문제점
    • 서버에서 사용자에 대한 정보를 가진다는 것은 서비스의 사용자가 증가하면 서버에 부하가 크다.
    • H/W적인 부분에서 업그레이드 및 교체가 필요하며 이를 수직 확장(Scale Up)이라고 한다.
      • 경제적 부담 + 한 대의 서버 운영으로 인한 단일 장애 지점을 갖는다. 이는 서버가 다운되면 모든 서비스가 다운된다는 의미
    • 사용자 증가로 인한 부하로 여러 서버를 운용하는 방법이 있고 이를 수평 확장(Scale Out)이라고 한다.
      • 수평 확장은 동일한 사양의 컴퓨터가 추가 되는 것으로 경제적 부담이 훨씬 적고 확장에 유연
      • 여러대의 서버를 이용하면 데이터의 불일치 문제가 발생할 수 있다.
        • 모든 서버가 메모리에 동일한 세션 정보를 갖고 있는 것이 아니다.
  • 토큰 기반 인증
    • 사용자의 정보를 서버에 저장하지 않는다.
    • 유저가 성공적으로 로그인하면, 서버는 클라이언트로 토큰을 발급
    • 클라이언트는 토큰을 받아 젖아하고, 서버에 요청할 때 HTTP header에 실어 함께 전송하면 서버는 이를 검증하고 유저를 인가한다.
    • 서버는 발급과 검증 두가지 역할만 할  뿐 직접 정보를 갖고 있지 않다.
    • 토큰은 세션과 달리 클라이언트에 정보가 저장되므로 노출되기 쉬워 민감한 정보를 담아서는 안된다.
    • 토큰 기반 인증을 사용하면 통신하면서 발생하는 오버헤드를 감안해야 한다.
    • Jwt 같은 경우 데이터를 직접 갖고 있는 클레임(Claim) 기반 토큰으로 토큰의 만료를 구현할 수 있다.

JWT의 구조

  • 토큰은 헤더(Header), 페이로드(Payload), 서명(Signature) 세 부분으로 구성되어 있으며 점(.)으로 분리된다. 따라서 Jwt는 헤더. 페이로드. 서명의 형태
  • 각 구성 요소를 한 줄의 String으로 나타내기 위해 Base64로 인코딩하며 url safe 형태이다.

 

  • Header
    • 헤더에는 일반적으로 토큰의 유형과 암호화 알고리즘 두 가지 정보를 담는다.
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("typ", "JWT");
headers.put("alg", "HS256");

 

  • Payload
    • 페이로드는 사용자의 정보 혹은 데이터 속성 등을 나타내는 클레임(Claim) 정보 단위로 구성
    • 클레임은 3가지로 구분되는데 등록된 클레임 (Registered Claim), 공개 클레임 (Public Claim), 비공개 클레임 (Private Claim)으로 구성되며 나는 막 구분해서 사용하지 않았기에 궁금하면 검색해보도록 하자.. 등록된 클레임 정도는 알아두면 좋을 것 같다!
Map<String, Object> payloads = new HashMap<String, Object>();
payloads.put("id", id);
payloads.put("auth", authority);

Date exp = calendar.getTime();	// expiration time

 

  • Signature
    • 특정 암호화 알고리즘을 사용하여 Base64 인코딩된 헤더와 페이로드 그리고 비밀키를 이용하여 암호화한다.
    • 비밀키는 공격자로 하여금 레인보우 테이블 기법의 해킹을 어렵게 하기위해 원문과 함께 비밀키를 더하여 해싱하는데 사용! (ex: HMAC(Keyed-hash Message Authentication Code))
      • 이는 비밀 키만 공유된다면 다수의 서버에서 토큰 유효성 검증 가능
        • 공유자원 불필요!! -> 서버 유지 보수 편리 확장성
return Jwts.builder()
                .setHeader(headers)
                .setClaims(payloads)
                .setSubject(type.isAccess() ? accessToken : refreshToken)
                .setExpiration(exp)
                .signWith(SignatureAlgorithm.HS256, key.getBytes())
                .compact();

 

검증

  • jwt.io에 접속하여 생성된 토큰과 비밀키를 입력하면 Header와 Payload에 값이 나타난다.

JWT 토큰을 Access 토큰으로 사용 시 문제점

  • JWT는 세션과 달리 Stateless 하다 서버에서는 아무런 정보가 없고, 토큰 자체의 만료일까지 토큰이 가지고 있기 때문에 토큰이 탈취당하면 토큰의 주인 계정에 접근할 수 있을 것이다.
  • 이미 발행된 토큰에 대한 제어를 할 수가 없다. -> 토큰이 탈취당한 걸 알아도 어떻게 처리하기 힘들다.

해결방안?

  • Refresh Token을 같이 생성하여 Access Token의 만료일을 짧게 설정하고, 만료된 토큰에 대해서 Refresh 하는 방식의 로직
    • 마찬가지로 Refresh Token이 탈취되면 같은 위험이 발생한다는 것.
    • Refresh Token을 따로 저장을 해야 하기 때문에 리소스 사용 (ex: DB, Redis)

완벽한 인증방식은 없는 듯하다.. 더 좋은 방식을 찾고 계속 보완해나가는 게 맞는 것 같다. 

이어서 Refresh Token의 활용방안과 한계에 대해서 정리해보도록 하겠습니다.!

반응형

'Spring' 카테고리의 다른 글

카카오 OAuth2.0 적용하기 - 1  (0) 2023.01.09
Spring Security 작동 원리  (0) 2022.11.23
Refresh Token이란?  (1) 2022.11.19
Spring MVC Life cycle  (0) 2022.11.17
Spring이란?  (0) 2022.11.17