카테고리 없음

2024-06-04 [Spring JWT (JSON Web Token)]

Glen_check 2024. 6. 4. 22:54

전날 프로젝트를 끝마치고 Kotlin + Spring 심화 과정으로 들어오면서 인증 인가 관련하여 보다 자세히 다루고 실습을 진행하기 때문에 기본기를 다시 한번 복습해보고 완벽하게 이해하고 넘어가고자 이번 포스팅은 다음과 같은 주제로 글을 작성하게 되었습니다. 

 


JWT(Json Web Token)

  • JWT(*JSON Web Token) 는 유저를 인증하고 식별하기 위한 토큰(Token) 기반 인증입니다.
  • 토큰 자체에 사용자의 권한 정보나 서비스를 사용하기 위한 정보가 포함됩니다.
  • RESTful 과 같은 Stateless 인 환경에서 사용자 데이터를 주고받을 수 있게 됩니다.
  • Session 을 사용하게 될 경우 쿠키 등을 통해 사용자를 식별하고 서버에 세션을 저장했지만, 토큰을 클라이언트에 저장하고 요청시 HTTP 헤더에 토큰을 첨부하는 것만으로도 단순하게 데이터를 요청하고 응답받을 수 있어, 확장성이 뛰어나고 메모리에 부담이 적습니다.

* JSON : JSON 은 "JavaScript Object Notation" 의 약자입니다. 직역하면 '자바 스크립트 객체 표기법'으로 데이터를 쉽게 '교환'하고 '저장' 하기 위한 텍스트 기반의 데이터 교환 표준 입니다. JSON 은 텍스트 기반이기 때문에 다양한 프로그래밍 언어에서 데이터를 읽고 사용할 수 있습니다.

 

서버의 확장성이라는 중요한 장점으로 최근 세션 기반의 인증 보다는 토큰 기반의 인증을 대부분 사용하며, 그중 JWT 형태를 가장 많이 사용합니다.

 

JWT(Json Web Token) 구조

JWT는 Header, Payload, Signature 총 세 파트로 나누어지며 각 파트는 점으로 구분하여 표현됩니다.

다음과 같이 JWT 는 HHHHH.PPPPP.SSSSS 형태의 문자열로 이루어진 토큰입니다. HHHHH 부분은 헤더(Header), PPPPP 부분은 내용(Payload), SSSSS 부분은 서명(Signature) 입니다.

 

Header

Header 는 두가지의 정보를 지니고 있어 두 키 값으로 구성됩니다.

typ: 토큰의 타입을 지정합니다. 보통 JWT 로 고정되어 사용됩니다, 확장성을 위해 만든 필드입니다.

alg: 서명 알고리즘을 지정합니다.  해싱 알고리즘으로는 보통 HMAC SHA256 혹은 RSA 가 사용되며 이 알고리즘은 토큰을 검증 할 때 사용되는 signature 부분에서 사용됩니다.

 

{
  "alg": "HS256",
  "typ": "JWT"
}

 

Payload

두 번째 부분은 JSON 형식으로 제공되는 페이로드(Payload)로 구성되어 있습니다. 페이로드에는 클레임(Claim)이 포함되어 있습니다. 클레임은 key, value 형태로 표현되는, 클라이언트가 서버에 전송하고 인증해야 하는 정보로 서버에서 설정한 사용자의 권한 정보 및 데이터가 담겨 있습니다.

일반적으로 사용자의 이름이지만 모든 데이터 세트를 포함할 수 있습니다.

 

클레임은 크게 Registered Claims Custom Claims 으로 나눌 수 있습니다.

  • Registered Claims
    • 토큰 자체의 정보를 담은 Claim 으로, 필수는 아니지만 Payload 에 포함하기 권장되는 claim 들입니다. 아래의 Claim 들이 존재합니다.
      • iss : Issuer, JWT 의 발급 주체를 표기합니다. 예를들어, naver.com 같은 형태가 될 수 있습니다.
      • sub : Subject, JWT의 대상 주체, 즉 유저를 표기합니다. 유저의 ID, 이름 등이 될 수 있습니다.
      • aud : Audience, 토큰을 받을 대상 그룹입니다. sub 와 혼동될 수 있는 개념인데요. 발급 주체가 동일하더라도 해당 토큰이 어디서 쓰일지는 다를 수 있습니다. 예를 들어, movie.naver.com 이라는 issuer 에서 토큰을 발급하더라도,  sports.naver.com 대상으로 발급을 할 수 도 있고, admin.naver.com 으로 발급을 할 수 도 있습니다. 보통 여러 시스템에 걸쳐 토큰을 사용할 수 있거나 한 시스템 내에서 토큰 별로 API 혹은 Resource 에 대해 권한이 나뉘는, 큰 시스템에서 사용합니다.
      • exp : Expiration time, JWT 가 만료되는 시간을 뜻하며, timestamp 형태로 표현합니다.
      • nbf : Not before time, 해당 시간 이전의 토큰은 활성화 되지 않음을 보장합니다.
      • iat : Issued at time, JWT가 발급된 시간입니다.
      • jti : JWT ID, Uniquie Identifier 즉 식별자로, 매번 랜덤으로 해당 jti를 생성하여 동일한 정보 이더라도 생성할때마다 다른 JWT가 나오도록 하는 역할입니다. *Replay Attack 을 방지하기 위해 사용됩니다.

* Replay Attack : Replay Attack 은 공격자가 보안 네트워크 통신을 가로채고, 해당 통신의 수신자로 하여금 공격자가 원하는 작업을 수행하도록 하기 위해 통신을 지연시키거나 재전송하는 방식입니다.

Replay Attack 은 네트워크에서 메시지를 가로챈 이후 메시지를 복호화 하기 위한 별도의 과정이 필요하지 않고, 메시지를 포함한 통신 전체를 다시 전송하는 것으로도 공격하는 것만으로도 성공할 수 있습니다.

 

  • Custom Claims
    • 필요에 의해 JWT 에 추가적으로 담는 claim 입니다.
    • 해당 claim이 *Application Context 외 일반적으로 쓰일 수 있는지에 따라 Public claims, Private Claims를 개념적으로 구분하기도 하며, 이곳에서 Registered Claim을 포함하여 일반적으로 정의된 Claim 목록을 확인할 수 있습니다.

 * Application Context : Application Context 란 Spring Application 전반에 걸쳐 모든 구성 요소의 제어 작업을 담당하는 IoC 엔진입니다.
더 구체적으로 설명하자면, IoC 컨테이너인 빈 팩토리(Bean Factory)가 존재하며 빈 팩토리를 상속받아 확장한 것이 Application Context 입니다.

 

Signature

  • 마지막 부분은 JSON Web Signature(JWS) 로 대표되는 암호화 서명입니다. 서명을 통해 서버는 자신이 보낸 JWT 가 맞는지를 검증할 수 있습니다.
  • Header 와 Payload 를 합쳐 암호화 한 후, Base64 로 한번 더 인코딩 한 값입니다.
  • 서명 알고리즘

알고리즘은 크게, 대칭 키를 사용한 알고리즘과 비대칭 키 쌍을 이용한 알고리즘으로 나뉘어 집니다. 비대칭 키를 이용한 암호화를 공개키 암호화 라고 부르기도 합니다.

 

  • 대칭키 알고리즘 (Sercret Key, Symmetric Key Algorithm)

이름에서도 알 수 있듯이 대칭되는 키를 통해 정보를 복호화하고 암호화하는 알고리즘입니다. 암호 알고리즘 중에서는 속도가 가장 빠르고 구현이 간단하지만, 키 관리가 어려워 키가 유출될 경우 보안에 취약할 수 있다는 단점을 가지고 있습니다.

예시로 사용자 "A" 와 사용자 "B" 는 동일한 키인 C를 가지고 있습니다. 사용자 A는 B에게 개인정보를 보내려하는데, 개인정보가 탈취되는 것을 막고자 대칭키 알고리즘을 통해 정보를 전송하려고 합니다.

사용자 "A", "B"는 동일한 키를 가지고 있어 사용자 "A"의 개인정보를 공통키인 C로 암호화한 다음 사용자 "B"에게 전송합니다.

 

사용자 "B"는 "A"와 동일한 키를 사용하여 복호화하여 "A"의 개인정보를 확인할 수 있습니다. 사용자의 개인정보를 확인하고, 다시 "정보를 잘 받았어!" 라고 응답을 보냅니다.

 

하지만 앞서 말한 것처럼 보안에 매우 취약할 수 있습니다. 예시로 Hacker "D" 가 탈취한 대칭키를 가지고 "A" 가 "B" 에게 개인정보를 전송할 때 중간에 개인정보를 변경,

사용자 "B" 는 복호화를 진행하여 정보를 봤는데 다른 이상한 정보가 적혀있고, 해당 정보를 그대로 사용하게 되면 무결성까지 깨지는 결과를 초래할 수 있습니다.

때문에 대칭키 알고리즘은 키를 철저히 보관하는 것이 핵심인 알고리즘입니다.

 

대표적으로 AES 대칭키 알고리즘이 있습니다.

 

  • 비대칭키 알고리즘 (Asymmetric Key Algorithm)

비대칭키의 경우 개인키(Private Key)와 공개키(Public Key)로 구성됩니다. 대칭키와는 다르게 한 키로 서명을 하면 다른 한 키로 복호화를 할 수 있습니다.

 

공개키 알고리즘

한 사용자는 공개키와 개인키로 구성된 키를 생성합니다.

Public Key : 공개키는 암호화에 사용되며, 누구나 이를 이용하여 데이터를 암호화 할 수 있습니다. 공개키는 외부에 공개되어 있으며 정보를 안전하게 전송하기 위해 사용됩니다.

Private Key : 개인키는 복호화에 사용되며, 키 소유자만 이를 알고 있습니다. 개인키는 안전하게 보관되어야하며, 이를 통해 암호화된 데이터를 복호화 할 수 있습니다.

 

예시로 "A" 와 "B"는 개인키와 공개키를 하나씩 생성한 상황입니다.

 

1. 암호화 과정 후 데이터 전송 

(1)"B"의 공개키를 이용해 사용자 "A"의 개인정보를 암호화합니다.

(2)"A"의 개인키를 이용해 개인정보를 한 번 더 암호화를 진행합니다.

(3) 암호화 된 데이터를 사용자 "B"에게 전송합니다.

 

2. 데이터 응답 후 복호화

(1) "A"의 공개키를 이용해 먼저 복호화합니다. 여기서 무결성을 지킬 수 있습니다.

(2) "B"의 개인키를 이용해 복호화를 진행합니다.

(3) "B"사용자는 "A"의 개인정보를 확인할 수 있습니다.

 

대표적으로 사용하는 것은 RSA 입니다.

 

JWT 활용 시 생각해보아야 할 부분 (Check List)

1. JWT 는 디코딩(변환)이 매우 쉽기 때문에 민감한 정보를 넣기 보다 최소한의 정보들을 넣는 것이 좋습니다.

2. SecrectKey 를 구성하는데 심혈을 기울여야 합니다.

3. 개인키(Private Key)와 공개키(Public Key)를 구분하여 사용하는 비대칭 알고리즘을 사용하는 것이 보안에 더욱 안전합니다.

4. JWT 는 유저 한 명의 토큰이 탈취당할 경우, Stateless 하다는 점 때문에 대처할 수 있는 방안이 별도로 없습니다. 탈취할 수 힘들도록 만들어 놓은 HttpOnly cookie 와 같은 저장소를 사용하거나,

해킹당한 유저의 토큰은 검증하는 과정을 거쳐 입장할 수 없도록 조치를 취할 수 있으나 이렇게 될 경우 JWT 를 이용하는 장점이 사라질 수 있습니다.