관리 메뉴

공부 기록장 💻

[Web/Backend/Auth] JWT란 무엇인가? 본문

# Tech Studies/Web

[Web/Backend/Auth] JWT란 무엇인가?

dream_for 2022. 8. 23. 11:26

** 목표: 세션 방식의 기존 인증 방법과 토큰 인증 방법의 차이를 이해하고, 토큰 인증 방법의 하나인 JWT의 개념과 동작 원리, 장점에 대해 공부한 후, NestJS 프레임워크에서 JWT 인증 기능을 구현해보자 !




JWT의 기본 개념


- JWT란, Json Web Token의 약자로, 모바일이나 웹의 사용자 인증을 위해 사용되는 전자 서명된 URL-safe (URL로 이용할 수 있으며 문자로만 구성된) 암호화된 토큰을 의미한다.
- 정보를 json 형태로 주고받기 위해 표준 규약에 따라 생성한 암호화된 토큰으로, 복잡하고 읽기 어려운 string 형태로 저장되어 있다.
- 일반적으로 클라이언트와 서버 사이에서 통신할 때 권한(인증)을 위해 사용하는 토큰이며, JWT 정보를 request에 담아 권한이 필요한 작업을 수행하게 하며 사용자에 대한 정보를 Self-Contained 방식으로 저장하는 Cliaim 기반 Web 토큰이다.



JWT의 구성 요소, 구조


JWT는 세 파트로 나누어지며, 각 파트는 . (점) 으로 구분하여 xxxx.yyyy.zzzz 와 같이 표현된다.
다음과 같이 헤더(header), 페이로드(payload), 서명(signature) 3가지로 구성되어 있다.

1. Header (헤더)

- 토큰의 헤더는 typ(토큰의 타입), alg (해시 알고리즘) 두 가지 정보를 담고 있다.

- typ: 토큰의 타입을 지정 (ex) JWT)
- alg: signature을 해싱하기 위한 알고리즘 방식을 지정 (HMAC, SHA256 또는 RSA와 같은 해싱 알고리즘)

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


2. Payload (정보)

- 전달하려는 정보(사용자 id 를 비롯한클레임 정보)가 들어있다.
- payload에 담는 정보 한 '조각'을 클레임이라 부르며, 이는 JSON 형태(key-value) 의 한 쌍으로 이루어져 있다.
- 클레임의 정보는 등록된(registered) 클레임, 공개(public) 클레임, 비공개(private) 클레임으로 세 종류가 있다.

1) 등록된 클레임 (Registered Claim)
- 토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터들로, 모두 선택적으로 작성이 가능하다.
- JWT를 간결하게 하기 위해, key의 길이는 모두 3인 string이다.
- 여기서 subjec로는 unique한 값을 사용하는데, 사용자 이메일을 주로 사용한다.

  • iss: 토큰 발급자(issuer)
  • sub: 토큰 제목(subject)
  • aud: 토큰 대상자(audience)
  • exp: 토큰 만료 시간(expiration) - NumericDate 형식으로 되어 있어야 하며, 언제나 현재 시간 이후로 설정되어 있어야 함 (ex) 1480849147370)
  • nbf: 토큰 활성 날짜(not before) - 이 날이 지나기 전의 토큰은 활성화되지 않음
  • iat: 토큰 발급 시간(issued at) - 토큰 발급 이후의 경과 시간을 알 수 있음
  • jti: JWT 토큰 식별자 (JWT id) - 중복 방지를 위해 사용하며, 일회용 토큰(Access Token) 등에 사용됨



2) 공개 클레임 (Public Claim)
- 사용자 정의 클레임(custom claim)으로, 공개용 정보를 위해 사용된다. 충돌 방지를 위해 URI 포맷을 이용하며, 다음과 같이 사용된다.

{
	"https://dream-and-develop.tistory.com" : true
 }


3) 비공개 클레임 (Private Claim)
- 사용자 정의 클레임(custom claim)으로, 서버와 클라이언트 사이에 임의로 지정한 정보를 저장한다.

{
	"token_type": access
 }



- payload의 내용은 수정이 가능하여, 더 많은 정보들을 추가할 수 있다.
- 다만, claim set은 최대한 가벼울 수록 좋기 때문에 큰 용량의 데이터를 사용하지 않는 것이 좋고, JWT는 쉽게 decode 되므로 sensitive 한 정보를 담지 않는 것에 유의해야 한다.
- 또한, 노출고 수정이 가능한 지점이므로, 인증이 필요한 최소한의 정보(id, pw 등 개인정보가 아닌 토큰을 가졌을 때 권한의 범위나 토큰의 발급일, 만료일자 등)만을 담아야 한다.



3. Signature (서명)

- JWT의 가장 핵심적이고 중요한 부분으로, signature은 secret key 를 포함하여 암호화되어 있다.
- header와 payload를 합친 후, 발급해준 서버가 지정한 secret key로, 암호화를 시켜 토큰을 변조하기 어렵게 만들어준다.
- 예를 들면, 토큰이 발급된 후 누군가 payload의 정보를 수정하면 payload에는 다른 누군가가 조작한 정보를 포함하게 되지만, signature에는 수정되기 이전의 payload 내용을 기반으로 이미 암호화된 결과가 저장되어 있으므로, 조작된 payload와는 다른 결과값이 나타나게 된다. 따라서 이러한 방식으로 비교를 하게 되면, 서버는 토큰이 조작되었는지 아닌지를 쉽게 판단할 수 있고, 다른 누군가는 조작된 토큰을 악용하기 어려워진다.


JWT의 동작 원리


다음 그림을 통해 JWT의 동작 원리를 이해해보자.

1. 사용자가 id와 password를 입력해 로그인 요청을 한다. (request)
2. 서버는 회원 DB에 저장되어 있는 사용자인지를 확인한다.
3. 확인이 되면, 서버는 로그인 요청 확인 후 secret key를 통해 JWT access token 을 발급한다.
4. 이를 클라이언트에 전달한다.
5. 사용자는 서비스 요청과 권한을 확인하기 위해, 헤더에 데이터(JWT) 요청을 한다.
6. 서버는 JWT Signature을 확인하고 Payload로부터 사용자 정보를 확인하여 access token을 검증한다.
7. 클라이언트 요청에 대한 응답과 요청한 데이터를 전달한다. (response)


위와 같은 방식으로, 토큰 기반 인증 방식은 사용자의 인증이 완료된 이후, 토큰을 발급한다.
클라이언트 쪽에서는 전달받은 토큰을 저장해두고, 서버에 요청을 할 때마다 해당 토큰을 서버에 함께 전달한다.
그 이후 서버는 토큰을 검증하고 응답하는 방식으로 작동한다.




기존의 인증 방식과 토큰 방식의 차이점



기존의 인증 방식 : 서버 기반 인증 방식

- 서버 기반의 기존 인증 시스템에서는 서버 측에서 유저들의 정보를 기억하고 있어야 한다.
- 사용자의 정보를 기억하기 위해서는 세션을 유지해야 하는데, 이를 위해서는 메모리, 디스크, DB 시스템 등을 통해 관리해야 한다.
- 기존 서버에 세션을 저장하는 방식에서는, 서버 여러 대를 사용하여 요청을 분산하므로, 어떤 유저가 로그인 했을 때 그 유저는 처음 로그인한 서버에만 요청을 보내도록 설정을 해야 한다.
- 클라이언트로부터 요청을 받으면, 클라이언트의 상태를 계속해서 유지하게 하고 이 정보를 서비스에 이용하는데, 이러한 서버를 Stateful Server 라고 한다.


기존 인증 방식의 문제점은?

1. 세션

  • 위에서 설명했듯, 사용자가 인증 할 때 서버는 인증 정보를 저장해둬야 하며, 이를 세션(session)이라 부른다. 대부분 메모리에 저장되는데, 로그인 중인 사용자가 늘어나는 경우 서버의 RAM에 부하가 걸린다. 이를 방지하기 위해, DB에 저장을 하기도 하는데, 이러한 방식 역시 데이터베이스에 무리를 줄 수 있다.

2. 확장성

  • 사용자가 늘어난다면, 더 많은 트래픽을 처리하기 위해 여러 프로세스를 돌리거나 서버용 컴퓨터를 추가하는 등 서버를 확장해야 한다. 세션을 사용한다면, 세션을 분산시키는 시스템을 설계해야 하지만 이러한 과정은 매우 어렵고 복잡하다.

3. CORS (Cross-Origin Resource Sharing)

  • 웹 어플리케이션에서 세션을 관리할 때 자주 사용되는 쿠키는, 단일 도메인 및 서브 도메인에서만 작동하도록 설계되어 있다. 따라서 쿠키를 여러 도메인에서 관리하는 것은 번거롭다/



개발 시스템의 규모가 커짐에 따라 기존의 서버 기반 인증 방식은 한계점을 보이기 시작하여, 토큰 기반의 인증 방식이 등장하게 되었다.


토큰 기반 인증 시스템

- 현대 웹 서비스에서 API를 이용한 웹 서비스를 개발할 때, 토큰을 사용하여 각 사용자들의 인증 작업을 처리하는 것이 가장 좋은 방법이다.
- 토큰 기반 인증 방식은 인증받은 사용자들에게 토큰을 발급하고, 서버에 요청(request)을 할 때마다 헤더에 토큰을 함께 보내도록 하여 유효성 검사를 한다.
- 토큰 기반 인증 방식은 사용자의 인증 정보를 서버나 세션에 담아두지 않기 때문에, 발급한 토큰 값만 알고 있다면 클라이언트 측에서 들어오는 요청만으로 (더이상의 로그인 처리가 필요 없음) 작업을 처리할 수 있다. 서버 기반의 인증 시스템과 달리 상태를 유지하지 않고 세션 스토리지가 필요 없으므로, stateless(무상태) 한 구조를 갖고 확장성이 있는 방식이다.



토큰 기반 인증 방식의 이점과 특징
1. 무상태성(stateless) & 확장성 (scalability)

  • 토큰은 클라이언트 측에 저장되므로, 서버는 완전히 Stateless하다.
  • 클라이언트와 서버의 연결고리가 없기 때문에 확장하기에도 매우 적합하다.
  • 서버 측 세션에 사용자 정보가 저장된 경우에 서버를 확장하여 분산처리 한다면, 해당 사용자는 처음 로그인 했었던 서버에만 요청을 받도록 설정하고 관리해주어야 하므로 이는 매우 복잡하다. 토큰의 경우, 어떠한 서버로 요청이 와도 상관이 없다.

2. 보안성

  • 클라이언트가 서버로 요청을 보낼 때, 쿠키를 전달하지 않으므로 쿠키 사용에 의한 취약점이 사라진다. 그러나 토큰 환경의 취약점도 존재하므로, 이에 대비해야 한다.

3. 확장성 (extensibility)

  • 로그인 정보가 사용되는 분야가 확장된다. 토큰에 선택적인 권한만 부여하여 발급이 가능하며, OAuth의 경우 Google, Kakao 등과 같은 Social 계정을 이용해 다른 웹서비스에서도 로그인을 가능하게 한다.

4. 여러 플랫폼 및 도메인에서 사용 가능

  • 서버 기반 인증 시스템의 문제점 중 하나인 CORS를 해결할 수 있는데, 애플리케이션과 서비스의 규모가 커지며 여러 디바이스를 호환시키고 더 많은 종류의 서비스를 제공하게 되는데, 토큰을 사용하면 어떤 디바이스, 어떤 도메인에서도 토큰의 유효성 검사를 진행한 후 요청을 처리할 수 있다.
  • 이런 구조를 통해 assets 파일(image, html, css, js 등)은 모두 CDN (content delivery network) 에서 제공하고, 서버 측에서는 API만 다루도록 설계할 수 있다.

일반 토큰 기반 VS 클레임 토큰 기반 | JWT의 장점


- 그렇다면, 일반 토큰 기반과 클레임 토큰 기반의 JWT 시스템의 차이점은 무엇인가?

JWT를 사용하는 가장 큰 이유는 클레임(Claim) 토큰 기반 인증이 주는 편리함이라고 할 수 있다.


기존에 사용하던 일반 토큰 기반 인증은 토큰을 검증할 때, 필요한 관련 정보들을 서버에 저장해두고 있었기 때문에
요청이 들어올 때마다 항상 DB에 접근해야만 했다.





- JWT 토큰의 경우, 값에 의한 호출 (By Value) 이 가능한 토큰이 발급된다. 토큰이 필요한 모든 정보를 담고 있으므로, 참조가 필요 없고, 마이크로서비스 자체에서 유효성을 검증한다.


클레임 토큰 기반으로 이루어진 JWT 는 사용자 인증에 필요한 모든 정보를 토큰 자체에 담고 있기 때문에, 별도의 인증 저장소가 필요없다. 분산 마이크로 서비스 환경에서 중앙 집중식 인증 서버와 데이터 베이스에 의존하지 않는 쉬운 인증을 제공하여 일반 토큰 기반 인증에 비해 편리하다고 할 수 있다.
이는, 여러 플랫폼 및 도메인 어플리케이션 규모가 커지면 여러 디바이스를 호환 시키고 더 많은 종류의 서비스를 제공해야 할 때, 어떤 디바이스에서도, 어떤 도메인에서도, 토큰만 유효하다면 요청이 정상적으로 처리되어 JWT의 장점이 더욱 두드러진다는 의미이다.




이를 아래 다이어그램으로 다시 비교하며 살펴보자.
기존의 인증 방식 의 경우, 발급된 토큰은 이후의 모든 서비스 호출에 사용된다.
서비스 요청에 대한 응답을 받기 위해서는, 토큰의 유효성을 확인하여 세부 정보를 쿼리해야 하는데, 이는 참조에 의한 호출 (By Reference) 형태로 모든 서비스는 상호 작용 시 다시 Auth Service 인증 시스템에 접속해야 한다.



JWT 토큰의 인증 방식을 사용하는 마이크로 서비스의 경우, 값에 의한 호출 (By Value) 이 가능한 토큰이 발급된다.
JSON 토큰이 인증/인가에 필요한 모든 정보를 담고 있으므로, 참조가 필요 없고, 마이크로서비스 자체에서 유효성을 검증한다.



따라서, 거대하고 복잡한 단일 애플리케이션 개발에서 서비스를 작은 단위로 분할하여 민첩한 개발을 가능케 하는 마이크로 서비스 아키텍처에서 API 클라이언트에 대한 Authentication (인증) 과 Authorization (인가/권한부여) 를 위한 매커니즘에, JWT 토큰 사용은 세션을 통한 방식과 달리 서버 측 부하를 낮출 수 있고 능률적인 접근 권한 관리를 하며, 분산/클라우드 기반 infrastructure 에 더 잘 대응할 수 있도록 하는 적합한 인증 방법이라고 할 수 있다.


JWT의 단점


- claim에 넣는 데이터 필드가 많아질 수록, JWT 토큰이 길어진다. (payload가 길어짐) 따라서 API 호출 시 매번 토큰 데이터를 서버에 전달해야 하는 길이가 길어지며, 네트워크 대역폭 낭비가 심할 수 있다.

- 토큰은 클라이언트에 저장되어, DB에서 사용자 정보를 조작하더라도 토큰에 직접 적용할 수 없다.

- 보안 JWT는 기본적으로 Payload에 대한 정보를 암호화하지 않는다. 단순하게 BASE64로 인코딩하기 때문에, 중간에 패킷을 가로채거나 다른 방법으로 토큰을 취득했다면, 디코딩을 통해 데이터를 엿볼 수 있게 된다. 따라서 JWE(JSON Web Encryption)을 통해 암호화 하거나 중요 데이터를 Payload에 포함시켜서는 안 된다.



https://jwt.io/
위 사이트에서 JWT 토큰을 검증하고 생성할 수 있게 해주는 디버거 서비스를 제공하고 있다.


[참고자료]

https://mangkyu.tistory.com/55
https://velog.io/@hahan/JWT%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80
https://mangkyu.tistory.com/56
https://debugdaldal.tistory.com/177
https://tech.toktokhan.dev/2021/04/30/JWT/

728x90
반응형
Comments