TIL20231231

2023. 12. 31. 17:33스프링

어제에 이어서 정리를 해보자면,
 
서버의 확장성이라는 중요한 장점으로 인해, 최근에는 세션 기반 인증 보다는 토큰 기반의 인증을 대부분 사용한다고 한다!
 
이러한 토큰은 어떤 형태든 될 수 있지만, JWT(JSON Web Token) 형태를 가장 많이 사용한다.
 
 

 
 

 
 
JWT는 XXXXX.YYYYY.ZZZZZ 형태의 문자열로 이루어진 토큰이다.
XXXXX 부분은 헤더(Header), YYYYY 부분은 내용(Payload), ZZZZZ 부분은 서명(Signature) 이다.
단순한 문자열인데 왜 Json Web Token 이라고 하는걸까?
헤더와 페이로드 부분이 사실은 JSON으로 이루어져 있기 때문이다..
 
JSON 자체를 단순한 문자열로 변환하기 위해 base64 라는 인코딩을 사용한다.
 
 

 
 

헤더(Header)


alg, typ 두 키값으로 구성된다.
alg : 서명 알고리즘을 명시한다.
typ : 토큰의 타입을 지정한다. 보통 “JWT” 로 고정이됩니다. 확장성을 위해 만든 필드다.

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

 
 
 

내용(Payload)

 
서버에서 설정한 사용자 권한 정보 및 데이터가 담겨있다.
이러한 key, value 형태로 표현되는 각각의 정보를 클레임(Claim) 이라 한다.

 
클레임은 크게 Registered ClaimsCustom Claims 으로 나눌 수 있다.
 
 
Registered Claims
토큰 자체의 정보를 담은 Claim으로, 필수는 아니지만 Payload에 포함하기 권장되는 claim들이다.

 
아래 이러한 Claim들이 존재한다.

  • iss : Issuer, JWT의 발급 주체를 표기한다. 예를들어, sparta.com 같은 형태가 될 수 있다.
  • sub : Subject, JWT의 대상 주체, 즉 유저를 표기한다. 유저의 ID등이 될 수 있다.
  • aud : Audience, 토큰을 받을 대상 그룹이다. sub 와 헷갈릴수 있는데,. 발급주체가 동일하더라도 해당 토큰이 어디서 쓰일지는 다를 수 있다.예를 들어, auth.sparta.com 이라는 issuer에서 토큰을 발급하더라도, course.sparta.com 대상으로 발급을 할 수 도 있고, admin.sparta.com 으로 발급을 할 수도 있다. 보통 여러 시스템에 걸쳐 토큰을 사용할 수 있거나 한 시스템 내에서 토큰 별로 API 혹은 Resource에 대해 권한이 나뉘는, 큰 시스템에서 사용한다.
  • exp : Expiration time, JWT가 만료되는 시간입니다. timestamp 형태로 표현한다.
  • nbf : Not before time, 해당 시간 전에는 JWT가 사용되어서는 안된다. 잘 사용하지 않다. 예약작업 등에 사용할 수 있다.
  • iat : Issued at time, JWT가 발급된 시간이다.
  • jti : JWT ID, Uniquie Identifier 즉 식별자로, 매번 랜덤으로 해당 jti를 생성하여 동일한 정보 이더라도 생성할때마다 다른 JWT가 나오도록 하는 역할이다. Replay Attack(보내는 메시지를 가로채 동일한 메시지를 다시 보내는 방식)을 방지하기 위해 쓰인다. 


일반적으로, iss, sub, exp 와 iat 둘중 하나를 사용하고, jti를 추가하기도 합니다. 나머지는 시스템 요구사항에 따라 선택적으로 사용한다.

 
 
Custom Claims

  • 필요에 의해 JWT에 추가적으로 담는 claim 이다.
  • 해당 claim이 Application Context외에 일반적으로 쓰일 수 있는지에 따라 Public claims, Private Claims를 개념적으로 구분하기도 하나, 큰 의미는 없다. 여기서 Registered Claim을 포함하여 일반적으로 정의된 Claim 목록을 볼 수 있다.
  • 예를들어 유저의 email 정보, roles (권한) 정보등을 필요에 따라 추가할 수 있다. 비밀번호 같은 민감정보는 담기면 안된다...또  권한을 통해 보통 인가(Authorization)를 수행하기도 한다!

{
  "sub": "1234567890",
  "iat": 1516239022,
  "name": "John Doe",
}
 
 
 

서명(Signature)
 

서버가 자신이 보낸 JWT가 맞는지를 검증하기 위한 값이다.
Header와 Payload를 합쳐 암호화 한 후 Base64로 한번 더 인코딩 한 값이다.

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

대칭키(Sercret Key, Symmetric Key) : 한 개의 키로 암호화 및 복호화가 가능할때, 이를 대칭키라 한다. 여기서 키는, 단순한 문자열이라고 보면 된다. 암호화라는 함수에 키와 메시지가 인자로 들어가고, 알 수 없는 문자열이 함수의 결과로 나온다고 보시면 됩니다! 아무나 어떤 메시지를 암호화를 하면 안되니, 키의 관리가 중요해진다.

키를 잘 관리만 한다면, 자신이 암호화한 메시지를 자신만 복호화를 할 수 있고, 만약 메시지 뒤에 암호화한 암호문을 같이 붙여 놓았다가 (메시지 + 암호문), 나중에 해당 암호문만 풀어서 메시지랑 비교하면 (메시지 == 복호화함수(암호문)), 이 메시지가 내가 보냈던 암호문이 맞는 것을 알 수 있다. 

이렇게 암호화를 이용하여 자기 자신이 맞다는 것을 표기한 부가적인 암호문을 서명(Signature)이라 한다. 그리고 복호화 하여 해당 서명이 메시지와 일치하는 지 확인하는 과정을 서명 검증(Verification)이라 한다. 특히, 이렇게 대칭키로 서명하는 과정을 대칭키 서명 이라고도 한다.
 

비대칭키 (Asymmetric Key) : 공개키의 경우 개인키(Private Key)와 공개키(Public Key)로 구성된다. 대칭키와 다르게, 한 키로 서명을 하면, 다른 한키로 복호화를 할 수 있다. 이 특성을 이용해 두 가지를 할 수 있다.

공개키 암호화: 철수가 공개키를 다른 영희에게 주고, “나한테 메시지를 보낼때는 이 공개키로 암호화를 해서 보내!” 라고 한 후 개인키를 철수만이 알도록 보관을 한다면, 영희가 보내는 메시지(암호문)는 철수가 가진 키로만 복호화를 할 수 있으므로 철수만이 영희가 쓴 원본 메시지를 볼 수 있다.

서명: 철수가 메시지와 개인키로 해당 메시지를 암호화한 암호문을 영희에게 보내면서,  “앞으로 메시지를 받을 때에는 공개키를 이용해서 내가 보낸 암호문을 복호화 하고, 내가 보낸 메시지와 정말로 일치하는지 확인해봐!” 라고 할 수 있다. 메시지가 서로 일치하면, 영희는 이 메시지가 철수가 보낸게 맞구나!를 확인할 수 있다. 만약 다른 개인키로 암호화를 했다면, 복호화가 안되는데, 

이 과정을 비대칭키 서명 혹은 전자서명이라 합니다.

이러한 서명 과정을 통해,  JWT를 검증할 수 있다. 메시지와 암호문을 보내 비교를 하는 과정에서 메시지는
Header + Payload, 암호문(서명값)은 Signature 가 된다.

그럼 대칭키를 이용한 서명과 비대칭키를 이용한 서명 중 어떤걸 선택하는 것이 나을까?

대칭키: 키 관리가 중요하므로, 단일 시스템 내에서 통신할때 적합하다.
비대칭키: 공개 키를 공유할 수 있으므로, 다수의 시스템이 간에 토큰을 공유하고 검증해야할 때 적합하다.

대칭 키 서명으로 JWT에서 주로 사용하는 알고리즘은 HS256, 비대칭키로 주요 사용하는 알고리즘은 RS256 이다!
 
256이 붙은 이유는, 위에서 설명한 암호화 과정 이외에 SHA256이라는 해시알고리즘을 추가로 이용하기 때문이다. 해시 알고리즘은 어떤 문자열이든, 동일한 길이로 만드는 암호화 알고리즘으로, 만들 순 있지만, 반대로 풀 순 없는 비가역적인 알고리즘이다. 동일메시지를 해쉬를 통해 암호화 할때에는 동일 결과가 나오고, 두 다른 메시지를 암호화 할때 서로 동일할 확률이 현저히 적습니다. 검증 과정에서 원문 자체를 비교하기 보다, 두 메시지를 해시 알고리즘을 통해 일정한 길이로 변환하여 비교한다고 보면 된다.
 
결론적으로, 서버는 JWT의 Header와 Payload를 자신의 키로 암호화하여 Payload뒤에 암호화된 서명(Signature)를 붙이고, 다시 JWT 관련 요청을 받았을 때, 자기 자신이 보낸게 맞다는 검증을 한다고 볼 수 있다.
 또한, exp 를 통한 토큰의 만료 여부도 함께 검증한다.
 

'스프링' 카테고리의 다른 글

TIL 20240108- 뉴스피드 프로젝트 시작  (0) 2024.01.08
TIL 20240105  (0) 2024.01.05
TIL 20231229  (0) 2023.12.29
TIL 20231228  (0) 2023.12.28
TIL 20231227  (0) 2023.12.27