2 minute read

[OAuth] 같이 참고하시면 좋을 OAuth에 관한 포스트입니다.

[JWT]

1. JWT(JSON Web Token)란?

JWT는 하나의 인터넷 표준 인증 방식입니다. JWT의 필요성에 대해서는 앞선 OAuth 관련 포스트에서 설명 드린 바 있습니다. 세션ID를 세션DB에 저장할 필요 없이 사용자의 인증을 수행할 수 있는 도구입니다. 사용자 인증에 필요한 모든 정보를 토큰 안에 포함하고 있기 때문입니다. 처음에는 어려운 개념이기는 한데, 하나하나 천천히 알아가봅시다.

2. 구조

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaXJzdE5hbWUiOiJoYW5uYWgiLCJpYXQiOjE2NTc5ODk2NDksImV4cCI6MTY1Nzk4OTY1Mn0.ObFDkEkddi4T31CNu9ol8-Oo-Tp-4-h1ElLY7DeDDJ4

제가 방금 임의로 만들어본 토큰입니다. 길고 이상하게 생겼죠? 자세히 보면 중간에 (.)이 두 개가 있어서, 세 부분으로 나뉘어져 있음을 알 수 있습니다. 여기서 첫번째 부분이 header, 두번째 부분이 payload, 세번째 부분이 signature가 됩니다. 직관적으로 보자면, 아래와 같은 구조입니다.

xxx(header-헤더).yyy(payload-내용).zzz(signature-서명)

jwt.io에서는 jwt토큰을 디코딩해주는 기능을 제공하고 있습니다. 제가 방금 임의로 만들어본 토큰을 붙여넣기 해볼까요?

Alt text

결과는 이렇습니다. 여기서 주목해야 하는 점이 있습니다. header와 payload는 base64인코딩된 값이지, 암호화된 값이 아니라는 점입니다. 따라서 중요한 정보를 넣어둬선 안됩니다.

2-1. Header

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

헤더엔 보시다시피 토큰의 타입과, 서명에 쓰인 알고리즘이 포함되어 있습니다. HS256은 제가 확인했던 라이브러리들에 default값으로 지정되어 있던 알고리즘인데요, 다양한 암호화 알고리즘을 지원하므로 따로 지정할 수 있습니다.

2-2. Payload

{
  "firstName": "hannah",
  "iat": 1657989649,
  "exp": 1657989652
}

Payload는 claims들로 구성이 되어 있습니다. 토큰의 정도를 key-value 형태로 저장합니다. firstName은 제가 임의로 만든 claim인데요, iat과 exp는 만료 시간을 지정하니 자동으로 생성이 되었습니다. 이것들이 무엇이고, JWT는 어떤 표준스펙으로 claim들을 가지고 있는지 간단하게 알아보도록 하겠습니다.

  • iss : 토큰 발급자 (issuer)
  • sub : 토큰 제목 (subject)
  • aud : 토큰 대상자 (audience)
  • exp : 토큰의 만료시간 (expiraton), 시간은 NumericDate 형식으로 되어있어야 하며 (예: 1480849147370) 언제나 현재 시간보다 이후로 설정되어있어야합니다.
  • nbf : Not Before 를 의미하며, 토큰의 활성 날짜와 비슷한 개념입니다. 여기에도 NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않습니다.
  • iat : 토큰이 발급된 시간 (issued at), 이 값을 사용하여 토큰의 age 가 얼마나 되었는지 판단 할 수 있습니다.
  • jti : JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용됩니다. 일회용 토큰에 사용하면 유용합니다.

jwt 토큰 검증 라이브러리를 살펴보시면 아시겠지만, payload안에 있는 이 claim들로 여러가지 유효성 검증이 가능합니다.

2-3. Signature

마지막 부분이자, JWT의 유효성을 secretkey로 검증할 수 있는 중요한 부분입니다. 생성과정을 보면 다음과 같습니다.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

header의 인코딩값, payload의 인코딩값을 합친 후, secretkey로 해싱을 합니다. 다시 base64인코딩을 해줍니다. 그럼 서명값은 어떻게 검증할까요? 저는 HS256을 알고리즘으로 선택했는데요, 해싱은 엄밀히 따지자면 암호화가 아닙니다. 암호화라는 말에 복호화의 가능성이 내재되어 있다는 면에서 특히 그렇습니다. 서명을 할 때 사용한 header값과 payload는 그대로 jwt에 포함되어있으니, 우리는 우리가 가지고 있는 secret키로 다시 해싱을 해서, 서명값을 비교를 한다면 위변조 여부를 알 수 있겠지요.

3. 장단점

3-1. 장점

  • 세션DB와 같은 저장소가 필요없습니다.(고도화된 경우에 Refresh Token을 사용한다면 얘기는 달라지겠지만…)
  • 토큰 자체에 만료기능이 내장되어 있습니다.
  • 쿠키를 사용하지 않으므로 쿠키에 의한 보안 취약점이 제거됩니다.

3-2. 단점 및 사용시 주의사항

  • URL 파라미터로 보내거나 헤더에 넣어 보냅니다. (따라서 Jwt생성에서 base64인코딩시 padding문자가 붙을 수 있으므로, Base64URLEncode를 해줘야 합니다.)
  • Payload에 기밀에 보장되어야 하는 정보는 넣지 않습니다.
  • 토큰이 탈취당하면 대처가 불가능합니다. 따라서 기밀성이 매우 중요한 서비스에서는 특히 주의해야합니다. 만료시간을 짧게 정하는 것은 하나의 보완책이 될 수 있습니다.
  • 많은 claim이 추가되면 토큰이 커질 수 있습니다.
  • stateless의 특성상 많은 요청에 대해서 jwt의 전송이 요구될 것이므로, 데이터 트래픽에 영향을 미칠 수 있습니다.

참고

[JWT] JSON Web Token 소개 및 구조

JWT algorithm: HS256, RS256

JWT(Json Web Token) 알아가기


광고


Comments