-
JWT ( JSON WEB TOKEN ) 란 ??Web/Backend 2020. 10. 3. 16:58
개념
JWT - Json 포맷을 이용하여 사용자에 대한 속성을 저장하는 Claim 기반의 Web Token
자가수용적(self-contained) - 필요한 모든 정보를 자체적으로 지니고 있다.
웹서버의 경우 http 헤더에 넣어서 전달할 수도, url의 파라미터로 전달할 수도 있다.
-- 참고 --
Claim 기반 = 사용자 정보나 데이터 속성 등을 의미.
클레임 토큰 = 토큰 자체가 저런 정보를 가지고 있는 방식인데, JWT는 이 Claim을 JSON을 이용해서 정의 합니다.
용도
- 회원 인증: JWT 를 사용하는 가장 흔한 시나리오 입니다. 유저가 로그인을 하면, 서버는 유저의 정보에 기반한 토큰을 발급하여 유저에게 전달해줍니다. 그 후, 유저가 서버에 요청을 할 때 마다 JWT를 포함하여 전달합니다. 서버가 클라이언트에게서 요청을 받을때 마다, 해당 토큰이 유효하고 인증됐는지 검증을 하고, 유저가 요청한 작업에 권한이 있는지 확인하여 작업을 처리합니다.
서버측에서는 유저의 세션을 유지 할 필요가 없습니다. 즉 유저가 로그인되어있는지 안되어있는지 신경 쓸 필요가 없고, 유저가 요청을 했을때 토큰만 확인하면 되니, 세션 관리가 필요 없어서 서버 자원을 많이 아낄 수 있죠. - 정보 교류: JWT는 두 개체 사이에서 안정성있게 정보를 교환하기에 좋은 방법입니다. 그 이유는, 정보가 sign 이 되어있기 때문에 정보를 보낸이가 바뀌진 않았는지, 또 정보가 도중에 조작되지는 않았는지 검증할 수 있습니다.
JWT 구조
jwt 는 .을 구분자로 3가지의 문자열로 되어있다.

- 헤더
typ : 토큰의 타입 지정
alg : 해싱 알고리즘 지정. 보통 HMAC SHA256 혹은 RSA 가 사용됨.
이 알고리즘은 토큰을 검증할 때 사용되는 signature 부분에서 사용됨.
{ "typ": "JWT", "alg": "HS256" }// HMAC SHA256 이 사용된 예제
- 정보
Payload 부분에는 토큰에 담을 정보가 들어있음.
여기에 담는 정보의 '한 조각'을 클레임(claim)이라고 부르고, 이는 name/value의 한 쌍으로 이뤄져있음.
클레임은 총 3가지로 나누어진다.
-- (1) 등록된 클레임(Registered Claim)
등록된 클레임은 토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터들로, 모두 선택적으로 작성이 가능하며 사용할 것을 권장한다. 또한 JWT를 간결하게 하기 위해 key는 모두 길이 3의 String이다. 여기서 subject로는 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)
공개 클레임은 사용자 정의 클레임으로, 공개용 정보를 위해 사용된다. 충돌 방지를 위해 URI 포맷을 이용하며, 예시는 아래와 같다.
{ "https://limdef.tistory.com": true }-- (3) 비공개 클레임(Private Claim)
비공개 클레임은 사용자 정의 클레임으로, 서버와 클라이언트 사이에 임의로 지정한 정보를 저장한다. 아래의 예시와 같다. 공개 클레임과는 달리 이름이 중복되어 충돌이 될 수 있으니 사용할때에 유의해야한다.
예시
{ "username": "lim" }예제 Payload ( 2개의 등록된 클레임 , 1개의 공개클레임, 2개의 비공개 클레임 )
{ "iss": "tistory.com", "exp": "1485270000000", "https://limdef.tistory.com": true, "userId": "11028373727102", "username": "lim" }- 서명(signature)
서명(Signature)은 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드이다.
서명(Signature)은 위에서 만든 헤더(Header)와 페이로드(Payload)의 값을 각각 BASE64로 인코딩하고,
인코딩한 값을 비밀 키를 이용해 헤더(Header)에서 정의한 알고리즘으로 해싱을 하고,
이 값을 다시 BASE64로 인코딩하여 생성한다.

jwt 토큰 예시 변조방지는 어떻게?
-> "비밀키"
위의 Claim 기반의 토큰을 봤으면, 첫번째 들 수 있는 의문이 토큰을 받은 다음에 누군가 토큰을 변조해서 사용한다면 어떻게 맞느냐? 입니다. 이렇게 메세지가 변조되지 않았음을 증명하는 것을 무결성(Integrity)라고 하는데, 무결성을 보장하는 방법 중 많이 사용되는 방법이 서명(Signature)이나 HMAC을 사용하는 방식입니다. 즉 원본 메시지에서 해쉬값을 추출한 후, 이를 비밀 키를 이용해서 복호화 시켜서 토큰의 뒤에 붙입니다. 이게 HMAC방식인데, 누군가 이 메시지를 변조를 했다면, 변조된 메시지에서 생성한 해쉬값과 토큰 뒤에 붙어있는 HMAC 값이 다르기 때문에 메시지가 변조되었음을 알 수 있습니다. 다른 누군가가 메시지를 변조한 후에, 새롭게 HMAC 값을 만들어내려고 하더라도, HMAC은 앞의 비밀키를 이용해서 복호화 되었기 때문에, 이 비밀키를 알 수 없는 이상 HMAC을 만들어 낼 수 없습니다.
서명 생성 방식
그러면 무결성 보장을 위해서 사용할 수 있는 알고리즘이 SHA1-256 HMAC 뿐일까요?
보안요건에 따라서 SHA1-256,384,512. 그리고 공인 인증서(Certification)을 이요한 RS256 등등 다양한 서명 방식을 지원합니다.
그렇다면 JWT 토큰이 어떤 방식으로 서명되어있는지 어떻게 알 수 있을까요?
그래서 JWT 토큰의 맨 앞부분에는 서명에 어떤 알고리즘을 사용했는지를 JSON형태로 정의한 후, 이 JSON을 다시 BASE64 방식으로 인코딩한 문자열을 붙입니다.
JWT의 문제점
사용이 쉽고, 서버의 개발 부담을 덜어줄 수 있다는 여러가지 장점을 가지고 있으나, 그만큼 또 단점도 가지고 있습니다.
1. 길이
Claim에 넣는 데이터가 많아질수록, JWT 토큰의 길이가 길어집니다. API 호출등에 사용할 시에, 매 호출마다 헤더에 붙어서 가야하기 때문에, 길이가 길다는 것은 그만큼 네트워크 대역폭 낭비가 심하다는 의미입니다.
2. 한번 발급된 토큰은 값을 수정하거나 폐기가 불가
JWT는 토큰 내에 모든 정보를 다 가지고 있기 때문에, 한번 발급된 토큰에 대한 변경은 서버에서는 더이상 불가능합니다. 예를 들어 토큰을 잘못 발행해서 삭제하고 싶더라도, Sinagture만 맞으면 맞는 토큰으로 인식을 하기 때문에, 서버에는 한번 발급된 토큰의 정보를 바꾸는 일등이 불가능합니다.
그래서 만약에 JWT를 쓴다면, Expire time을 꼭 명시적으로 두도록하고, refresh token등을 이용해서, 중간중간 토큰을 재발행하도록 해야합니다.
3. 보안
JWT는 기본적으로 Claim에 대한 정보를 암호화하지 않는다. 단순히 BASE64로 인코딩만 하기 때문에, 중간에 패킷을 가로채거나, 기타 방법으로 토큰을 취득했으면 토큰 내부 정보를 통해서 사용자 정보가 누출 될 수 있는 가능성이 있습니다. 특히 자바스크립트 기반의 웹 클라이언트의 경우 브라우저상의 디버거등을 통해서 토큰이 노출될 가능성이 높습니다. 그래서, 이를 보완하는 방법으로는 토큰 자체를 암호화하는 방법이 잇습니다. JSON을 암호화하기 위한 스펙으로는 JWE(JSON Web Encryption)이 있습니다.
- OAtuh 와 Jwt 차이점
OAuth에 의해서 발급되는 access_token은 random string으로 토큰 자체에는 특별한 정보를 가지고 있지 않는 일반적인 스트링 형태입니다. 아래는 페이스북에서 발급된 access_token의 형태로 일반적인 문자열 형태임을 확인할 수 있습니다.
OAuth 토큰의 경우

OAtuh 과정 1. API 클라이언트가 Authorization Server(토큰 발급서버)로 토큰을 요청합니다. 이때, 토큰 발급을 요청하는 사용자의 계정과 비밀번호를 넘기고, 이와 함께 토큰의 권한(용도)를 요청합니다. 여기서는 일반 사용자 권한(enduser)과 관리자 권한(admin)을 같이 요청하였습니다.
2. 토큰 생성 요청을 받은 Authorization Server는 사용자 계정을 확인한 후, 이 사용자에게 요청된 권한을 부여해도 되는지 계정 시스템 등에 물어본 후, 사용자에게 해당 토큰을 발급이 가능하면 토큰을 발급하고, 토큰에 대한 정보를 내부(토큰 저장소)에 저장해 둡니다.
3. 이렇게 생성된 토큰은 API 클라이언트로 저장이 됩니다.
4. API 클라이언트는 API를 호출할때 이 토큰을 이용해서 Resource Server(API 서버)에 있는 API를 호출합니다.
5. 이때 호출되는 API는 관리자 권한을 가지고 있어야 사용할 수 있기 때문에, Resource Server가 토큰 저장소에서 토큰에 관련된 사용자 계정, 권한 등의 정보를 가지고 옵니다. 이 토큰에 (관리자)admin 권한이 부여되어 있기 때문에, API 호출을 허용합니다.
6. API 서버는 응답을 보냅니다.

1. 토큰을 생성 요청하는 방식은 동일합니다. 마찬가지로 사용자를 인증한 다음에 토큰을 생성합니다.
2. 다른 점은 생성된 토큰에 관련된 정보를 별도로 저장하지 않는다는 것입니다. 토큰에 연관되는 사용자 정보나 권한 등을 토큰 자체에 넣어서 저장합니다.
3. API를 호출하는 방식도 동일합니다.
4. Resource Server (API 서버)는 토큰 내에 들어있는 사용자 정보를 가지고 권한 인가 처리를 하고 결과를 리턴합니다.
결과적으로 차이점은
1. 토큰을 생성하는 단계에서는 생성된 토큰을 별도로 서버에서 유지할 필요가 없다.
2. 토큰을 사용하는 API 서버 입장에서는 API 요청을 검증하기 위해서 토큰을 가지고 사용자 정보를 별도로 계정 시스템 등에서 조회할 필요가 없다.
참고 및 출처
'Web > Backend' 카테고리의 다른 글
스프링부트 @Component 와 @Configuration , @Bean (0) 2021.01.31 Springboot - ApplicationTest에서의 에러 // test 건너 뛰기 (0) 2020.11.04 nginx (1) 특징 공부하기 (0) 2020.10.01 스칼라 서브쿼리, 연관 서브쿼리 동작원리 및 실행순서 (0) 2020.08.10 JPA 사용법 차근차근 알아보기(1) - Entity, save (0) 2020.07.28 - 회원 인증: JWT 를 사용하는 가장 흔한 시나리오 입니다. 유저가 로그인을 하면, 서버는 유저의 정보에 기반한 토큰을 발급하여 유저에게 전달해줍니다. 그 후, 유저가 서버에 요청을 할 때 마다 JWT를 포함하여 전달합니다. 서버가 클라이언트에게서 요청을 받을때 마다, 해당 토큰이 유효하고 인증됐는지 검증을 하고, 유저가 요청한 작업에 권한이 있는지 확인하여 작업을 처리합니다.