70일차

그루트·2021년 11월 23일
0

JWT의 등장

JWT(Json Web Token)은 위와 같은 일련의 과정속에서 나타난 하나의 인터넷 표준 인증 방식입니다. 말그대로 인증에 필요한 정보들을 Token에 담아 암호화시켜 사용하는 토큰인 것이죠.

따라서 사실 기본적인 인증을 진행하는 구조는 Cookie때와 크게 다르지는 않습니다. 다만, 강조되는 점은 JWT는 서명된 토큰이라는 점입니다. 공개/개인 키를 쌍으로 사용하여 토큰에 서명할 경우 서명된 토큰은 개인 키를 보유한 서버가 이 서명된 토큰이 정상적인 토큰인지 인증할 수 있다는 이야기이죠.

이러한 JWT의 구조 때문에 인증 정보를 담아 안전하게 인증을 시도하게끔 전달할 수 있는 것입니다.

그럼 구조가 어떻게 되어있는지 확인해보겠습니다.

JWT는 각각의 구성요소가 점(.)으로 구분이 되어있으며 구성 요소는 다음과 같습니다.

  • Header

  • Payload

  • Signature

아래처럼 각각의 구성 요소가 .으로 구분되어있는 형태이죠.

xxxxx.yyyyy.zzzzz

그럼 지금부터는 상세하게 각 구성요소에 어떤 값이 들어있는지 확인해보도록 하겠습니다.

1. Header

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

header에는 보통 토큰의 타입이나, 서명 생성에 어떤 알고리즘이 사용되었는지 저장합니다.

지금같은 경우에는 현재 토큰의 타입이 JWT이고, 앞서 이야기했던 개인키로 HS512 알고리즘이 적용되어 암호화가 되어있다고 확인할 수 있겠네요 😀

2. Payload

{
  "sub": "1",
  "iss": "ori",
  "exp": 1636989718,
  "iat": 1636987918
}

payload에는 보통 Claim이라는 사용자에 대한, 혹은 토큰에 대한 property를 key-value의 형태로 저장합니다. Claim이라는 말 그대로 토큰에서 사용할 정보의 조각인 셈이죠.

사실 어떤 Claim값을 넣는지는 개발자의 마음(?)이긴 하지만 일단은 배울 때는 JWT의 표준 스펙을 알아보도록 할게요.

표준 스펙상 key의 이름은 3글자로 되어있습니다. JWT의 핵심 목표는 사용자에 대한, 토큰에 대한 표현을 압축하는 것이기 때문에 이를 정의한 것이라고 볼 수 있겠네요.

  1. iss (Issuer) : 토큰 발급자
  2. sub (Subject) : 토큰 제목 - 토큰에서 사용자에 대한 식별값이 됨
  3. aud (Audience) : 토큰 대상자
  4. exp (Expiration Time) : 토큰 만료 시간
  5. nbf (Not Before) : 토큰 활성 날짜 (이 날짜 이전의 토큰은 활성화 되지 않음을 보장)
  6. iat (Issued At) : 토큰 발급 시간
  7. jti (JWT Id) : JWT 토큰 식별자 (issuer가 여러명일 때 이를 구분하기 위한 값)

이러한 표준 스펙으로 정의되어있는 Claim 스펙이 있다는 것이지, 꼭 이 7가지를 모두 포함해야하는 것은 아니고, 상황에 따라 해당 서버가 가져야할 인증 체계에 따라 사용하면 됩니다.

표준 스펙 외에도 필요하다 싶으면 추가해도 전혀 문제가 없습니다. 그건 개발자의 자유니까요. 예를 들어 뒤에서 이야기하겠지만 Access Token과 Refresh Token을 구분하고 싶다면 "token_type"라는 커스텀한 Claim을 만들고 값을 아래와 같이 "access token"으로 두어 구분지어도 괜찮습니다.

{
  "token_type" : "access token"
}

다만 가장 중요한 것은 payload에 민감한 정보를 담지않는 것입니다. 위에 header와 payload는 json이 디코딩되어있을 뿐이지 특별한 암호화가 걸려있는 것이 아니기 때문에 누구나 jwt를 가지고 디코딩을 한다면 header나 payload에 담긴 값을 알 수 있기 때문이죠.

아래의 디코딩 과정을 보면,

jwt.io에서 서버에서 생성한 JWT를 넣기만해도 볼 수 있는 화면입니다. JWT에서 header와 payload는 특별한 암호화없이 흔히 사용할 수 있는 base64 인코딩을 사용하기 때문에 서버가 아니더라도 그 값들을 확인할 수 있습니다.

그래서 JWT는 단순히 "식별을 하기위한" 정보만을 담아두어야하는 것입니다.

간혹 JWT의 header나 payload가 식별값만 존재하지만 해당 값들도 암호화를 통해 감추어야하지 않느냐는 생각을 가졌을 때가 있습니다.

예를 들어 base64UrlEncode(암호화(header)).base64UrlEncode(암호화(payload)).signature(암호화된 이 둘을 또 암호화)와 같은 형태였습니다.

제가 생각했던 방법이지만 사실 필요없는 행위입니다. 암호화는 민감한 정보를 막아두어야할 때는 필요한 행위이지만 이 자체만으로도 많은 리소스를 사용하기 때문에 신중하게 사용해야합니다. 매 http 요청마다 한번의 복호화가 더 추가되는 셈이니까요.

그렇기 때문에 유출되었을 때 그렇게 큰 상관이 없는 비민감정보를 토큰에 담는 것이 기본 스펙이 되는 것이고, 서버는 굳이 header나 payload를 암호화하지 않아도 되는 것입니다.

profile
i'm groot

0개의 댓글