시작으로


최근 Express.js로 사내 rest-api서버를 구축했었는데, 그 중 사용자 인증을 처리를 JWT로 구현했었다. 그때는 사용방법만 빠르게 익히고 구현했기 때문에 정리할 시간이 없었다.

현재 서비스를 오픈하고 시간이 남아서 지금이라도 정리를 해본다.(기억 < 기록 )

본 포스트는 JWT 공식문서를 번역 및 테스트하여 정리한 글이다.


JSON 웹 토큰이란?

JWT(JSON Web Token)는 당사자 간에 정보를 JSON 개체로 안전하게 전송하기 위한 간결하고 자체 포함된 방법을 정의하는 개방형 표준이다. 이 정보는 디지털 서명이 되어 있으므로 신뢰할 수 있다. JWT는 비밀(HMAC 알고리즘 사용)을 사용하거나 RSA 또는 ECDSA 를 사용하는 공개/개인 키 쌍을 사용하여 서명할 수 있다.


JSON 웹 토큰은 언제 사용하는가?

  • 인증(Authorization), 가장 일반적으로 사용하는 이유이다. 사용자가 로그인하면 서버에서 토큰을 발급해주고 후속 요청에는 JWT가 표함되어 사용자가 해당 토큰으로 허용되는 경로, 서비스 및 리소스에 엑세스할 수 있다.

  • 정보 교환, 당사자 간에 정보를 안전하게 전송하는 좋은 방법이다. 공개/개인 키 쌍을 사용하여 JWT에 서명할 수 있기 때문에 발신자가 누군지 확인할 수 있다. 또한, 헤더와 페이로드를 사용하여 서명을 계산하므로 콘텐츠가 변조되지 않았는지 확인할 수 있다.


JSON 웹 토큰의 구조

image

간결한 형태의 JSON 웹 토큰은 점(.)으로 구분된 세 부분으로 구성되어 있다.

  • 헤더, Header
  • 페이로드, Payload
  • 서명, Signature

따라서, JWT는 일반적으로 아래와 같다.

xxxxx.yyyyy.zzzzz

Header는 일반적으로 JWT인 토큰 유형과 HMAC SHA256 또는 RSA와 같이 사용 중인 서명 알고리즘의 두 부분으로 구성되어있다.

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

그 다음, 이 JSON은 JWT의 첫 번째 부분을 형성하도록 인코딩된 Base64Url이다.


Payload

JWT의 두 번째 부분은 클레임(Claims)을 포함한 Payload이다. 클레임(Claims)은 엔티티 및 추가 데이터에 대한 설명이다.
클레임(Claims)에는 registered, public, and private claims 3가지 유형이 있다.

  • 등록된 클레임(Registered claims) : 필수는 아니지만, 유용하고 상호 운용 가능한 클레임 집합을 제공하기 위해 권장되는 미리 정의된 클레입 집합이다. 그 중 일부는 iss(발급자), exp(만료 기간), sub(제목), aud(대상)기타이다.

  • 공개 클레임(Public claims) : JWT를 사용하는 사람들이 마음대로 정의할 수 있다. 그러나 충돌을 방지하려면 IANA JSON Web Token Registry에서 정의하거나 충돌을 방지할 수 있는 네임스페이스를 포함하는 URI로 정의해야 한다.

  • 비공개 클레임(Private claims) : 사용에 동의하고 등록된 클레임이나 공개 클레임이 아닌 당사자간에 정보를 공유하기 위해 생성된 맞춤 클레임이다.

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

그 다음, Payload는 Base64Url로 인코딩되어 JWT의 두 번째 부분을 형성한다.

서명된 토큰의 경우 이 정보는 변조로부터 보호되지만 누구나 읽을 수 있다. 암호화되지 않은 경우 JWT의 Payload 또는 header 요소에 비밀 정보를 넣지 말아야된다.


Signature

서명을 생성하려면 인코딩된 header, 인코딩된 Payload, secret, header에 지정된 알고리즘을 가져와서 서명해야 한다.
예를 들어 HMAC SHA256 알고리즘을 사용하려는 경우 서명은 다음과 같이 생성된다.

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

서명은 메시지가 도중에 변경되지 않았는지 확인하는 데 사용되며 개인 키로 서명된 토큰의 경우 JWT의 보낸 사람이 누군지 확인할 수도 있다.


모두 합치면

출력은 HTML 및 HTTP 환경에서 쉽게 전달할 수 있도록 점으로 구분된 3개의 Base64-URL 문자열이다.

다음은 header와 payload가 인코딩되어 있고 비밀로 서명된 JWT를 보여준 것이다.

image

JWT를 사용하고 이러한 개념을 실제로 적용하고 싶다면 jwt.io 디버거 를 사용하여 JWT 를 디코딩, 확인 및 생성할 수 있다.


JSON 웹 토큰은 어떻게 작동하는가?

  • 인증에서 사용자가 자격 증명을 사용하여 성공적으로 로그인하면 토큰을 생성하여 반환한다.
  • 일반적으로 토큰을 필요 이상으로 오래 보관하지 않는다.
  • 또한, 보안이 취약하기 때문에 민감한 세션 데이터를 브라우저 저장소에 저장해서는 안된다.
  • 사용자가 보호된 경로 또는 리소스에 엑세스하려고 할 때마다 사용자는 Bearer스키마를 사용하여 HTTP 헤더에 Authorization를 탑재하여 JWT를 보내야 한다.
  • HTTP 헤더를 통해 JWT 토큰을 보내는 경우 토큰이 너무 커지지 않도록 해야 한다. 일부 서버는 8KB 이상의 헤더를 허용하지 않는다.
  • 토큰이 Authorization헤더로 전송되면 CORS(Cross-Origin Resource Sharing)는 쿠키를 사용하지 않으므로 문제가 되지 않는다.
Authorization: Bearer <token>