본문 바로가기
IT 기본지식

JSON Web Token(JWT) 에 대해 살펴보자

by 내기록 2023. 10. 4.
반응형

목차 LIST

     

    JSON Web Token 이란 무엇인가?

    JSON Web Token(JWT)은 JSON 객체로 정보를 안전하게 전송하는 방법을 정의하는 공개된 표준(RFC 7519)입니다. 이 정보는 디지털로 서명이 되어있기 때문에 신뢰성이 높은 검증된 방법입니다. JWT는 HMAC 알고리즘으로 비밀키를 사용하여 서명될 수 있고, RSA나 ECDSA를 사용한 공개/비공개 키 쌍으로도 서명될 수도 있습니다.

     

    JWT는 두 당사자들 사이의 정보를 안전하게 전송하기 위해 "암호화"를 한다는 특징이 있습니다. 그리고 다른 중요한 특징은 "서명된 토큰"입니다.

    서명된 토큰(signed token)은 그 안에 포함된 내용의 무결성을 확인할 수 있게 해주며, 암호화된 토큰(encrypted token)은 해당 내용을 외부로부터 보호합니다. 특히, 토큰이 공개/비공개 키 쌍을 사용하여 서명될 경우, 이 서명은 비공개 키를 보유하고 있는 당사자만이 그 토큰을 서명했다는 것을 보장합니다.

     

    언제 JWT 토큰을 사용할까?

    - Authorization(권한 부여) : JWT를 사용하는 가장 일반적인 경우입니다. 먼저 사용자가 로그인을 하면, 그 후에 있을 모든 요청에는 JWT가 포함됩니다. 이를 통해서 사용자는 본인에게 권한이 있는 routes, service, resources에 접근할 수 있습니다. SSO(Single Sign On)은 JWT가 널리 사용되는 예시 중 하나인데, 오버헤드가 적으며 다른 도메인 간에도 유연하게 사용될 수 있습니다.

    [!] 다른 도메인 간에 유연하게 사용된다는건 무슨 뜻일까?

    예를 들어, 웹서비스 A와 웹서비스 B가 있을 때, 웹서비스 A에서 발급받은 JWT를 서비스 B에서도 인식하거나 사용할 수 있다는 것을 의미합니다. 이렇게 되면 사용자는 별도 로그인 없이토큰을 이용하여 A,B 서비스 모두 인증을 유지할 수 있습니다.

     

    - Information Exchange(정보 교환) : JWT를 사용하면 당사자 간에 정보를 안전하게 전송할 수 있습니다. JWT는 서명(signed) 될 수 있는데, 예를 들면 공개/비공개 키 쌍을 사용하여 서명할 수 있기 때문에 수신자는 발신자가 누구인지 알 수 있으며, 서명은 header와 payload를 사용하여 계산되기 때문에 내용이 변조되지 않았음을 확인할 수도 있습니다.

     

    JWT Structure?

    JSON 웹 토큰은 압축된 형태에서 점(.)으로 구분된 세 부분으로 나뉩니다.

    1. Header
    2. Payload
    3. Signature

    그러므로, JWT는 일반적으로 아래와 같은 형태를 가집니다.

    xxxxx.yyyyy.zzzzz

     

    Header

    보통 헤더는 두 부분으로 구성됩니다.

    - 토큰의 종류 : JWT

    - 사용되는 서명 알고리즘 : HMAC, SHA256, RSA

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

     

    그리고 이 내용들은 JWT를 구성하기 위해 Base64Url로 인코딩됩니다.

     

    Payload

    토큰의 두 번째 부분은 payload로 'claims'를 포함하고 있습니다. Claims(클레임)은 entity(일반적으로 사용자)에 대한 정보와 추가적인 데이터를 포함합니다.

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

    Payload는 Base64Url로 인코딩된 다음 JWT의 두 번째 부분으로 들어갑니다.

    [!] signed token은 조작으로부터 보호될 수 있지만, 누구나 읽을 수 있다는 것에 주의해야 합니다. JWT의 payload나 헤더에 암호화 되지 않은 secret information를 넣으면 안됩니다.

     

    클레임에는 세 가지 유형이 있습니다 : Registered claims, Public claims, Private claims

     

    1. Registered claims

    • 표준화된 클레임으로, 토큰에서 일반적으로 사용할 수 있도록 사전에 정의되어 있습니다. 필수는 아니지만 사용이 권장됩니다.
    • 이 클레임은 다양한 시스템 간에 호환되며 정보 교환이 가능하다는 이점이 있습니다.
    • 클레임은 iss(issuer발행자), exp(expiration time, 만료 시간), sub(subject, 주제), aud(audience, 청중) 등의 내용을 포함합니다.

    [!] 다양한 시스템 간에 호환되며 정보 교환이 가능하다는게 무슨 뜻일까?

    "Registered claims"는 JWT 표준에 따라 사전에 정의된 클레임들을 의미합니다. 이들 클레임은 JWT가 널리 사용되는 여러 시스템과 애플리케이션에서 공통으로 인식하고 이해할 수 있는 형식을 가지고 있습니다. 따라서 이들 클레임은 여러 시스템 간의 호환성을 보장하며, 이러한 호환성은 정보의 일관된 교환과 처리를 가능하게 합니다.
    예를 들면, iss (발행자) 클레임은 JWT를 발행한 주체를 가리킵니다. 이 클레임은 거의 모든 JWT 처리 시스템에서 인식되고 적절히 처리될 수 있습니다. 따라서 "registered claims"는 정보 교환의 표준화와 호환성을 위한 중요한 역할을 합니다.
    [!] iss? exp? 왜 세자로 줄이는거죠?
    JWT는 간결함을 목표로 하기 때문에 클레임의 이름을 세글자로 줄여 사용합니다.

     

    2. Public claims

    JWT를 사용하는 사람들에 의해 자유롭게 정의될 수 있습니다.

    그렇기 때문에, 공개 클레임 이름은 다양한 시스템이나 애플리케이션에서 클레임 이름의 중복을 피하기 위해 특정 기준에 따라 정의되어야 합니다. 중복을 방지하기 위해 IANA JWT Registry에 정의할 수 있습니다. 또는 클레임의 이름을 충돌을 피할 수 있는 namespace를 포함하는 URI로 정의하는 것을 권장합니다.

    예를 들면, https://example.com/claims/employeeID  같은 형식으로 클레임의 이름을 정의할 수 있습니다. 

     

    요악하면 public claims은 개발자나 조직이 자유롭게 정할 수 있지만, 중복을 피하기 위해 특정 규칙이나 관례를 따르는 것이 좋습니다.

     

    [!] IANA JSON Web Token Registry가 뭔가요?

    여기서 "충돌"은 다양한 시스템이나 응용 프로그램에서 같은 이름의 클레임을 사용할 때 발생하는 이름의 중복을 의미합니다.
    IANA JSON Web Token Registry는 이러한 클레임 이름의 중복을 방지하기 위한 공식 저장소입니다.

     

    3. Private claims

    사용에 동의한 당사자 간에 정보를 공유하기 위해 생성된 사용자 지정 클레임입니다.

     

    다시 위의 예시를 살펴보겠습니다.

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

    - 여기서 sub은 registered claim입니다. 

    - name, admin은 JWT 스펙에 사전 정의되지 않은 클레임이므로 public claim 또는 private claim 일 수 있습니다.

     

    Signature

    signature(서명) 부분을 생성하기 위해서는 인코딩된 헤더, 인코딩된 페이로드, 비밀키(secret), 헤더에 명시된 알고리즘을 사용하여 서명해야 합니다. 

    예를 들어, HMA SHA256 알고리즘을 사용하고자 한다면, 서명은 아래와 같은 방식으로 생성됩니다.

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

     

    서명은 중간에 메시지가 변경되지 않았음을 검증하는데 사용됩니다. 그리고 개인키로 서명된 토큰의 경우 JWT의 발신자가 예상하는 발신자가 맞는지 검증하는 데에도 사용됩니다.

     

    JWT 생성

    앞서 나온 내용들을 조합하여 JWT를 생성합니다. 

    결과는 세 개의 Base64-URL 문자열로 되어있으며, dots(.) 으로 구분됩니다. 이런 구조는 HTML과 HTTP 환경에서 쉽게 전달될 수 있으며, SAML과 같은 XML 기반에 비해 더 간결합니다.

     

    다음 내용은 앞서 봤던 헤더와 페이로드가 인코딩된 HWT를 보여줍니다. 이 JWT는 비밀키로 서명되었습니다.

     

    https://jwt.io/introduction

    jwt.io 사이트의 Debugger를 사용하면, 아래처럼 JWT 내용을 확인할 수 있습니다.

    JWT의 동작 방식

    인증에서 사용자가 자신의 자격 증명(credentials)을 사용하여 성공적으로 로그인하면 JSON Web Token이 반환됩니다. 토큰은 자격증명이기 때문에, 보안 문제를 예방하기 위한 주의(greate care)가 필요합니다. 일반적으로, 토큰은 불필요하게 오래 보관하지 않습니다.

     

    또한, 민감한 세션 데이터를 브라우저 저장소에 저장해서는 안됩니다.

     

    사용자가 권한이 필요한 경로(protected route)나 리소스에 접근하려 할 때마다, 사용자 에이전트는 JWT를 전송해야 합니다. 일반적으로, Authorization header에 Bearer 스키마를 사용해서 전송합니다. 

    헤더 정보는 다음과 같아야 합니다:

    Authorization: Bearer <token>

     

    특정 경우에, 이것은 무상태 인증 메커니즘(stateless authorization mechanism)일 수 있습니다. 서버에서 인증이 필요한 경로(protected routes)는 Autorication 헤더에서 유효한 JWT 정보를 확인하고, 만약 존재한다면(present) 사용자는 보호된 리소스에 접근할 수 있습니다. 만약 JWT가 필요한 데이터를 포함하고 있다면, 특정 연산을 위해 데이터베이스를 조회하지 않아도 되는 이점이 있습니다.

     

    [!] 필요한 데이터를 포함하고 있다는게 무슨 말이죠?

    JWT는 토큰 내에 데이터를 포함하고 있습니다. 사용자의 권한, ID 또는 다른 어떤 속성이 포함될 수 있습니다.
    이러한 속성 내에 사용자의 권한을 나타내는 writer, reader, admin 등의 정보를 같이 포함시키면 데이터베이스를 조회하기 않고도 사용자의 권한 등급을 알 수 있습니다.
    다시 말하면, 사용자가 어떤 특정 작업을 수행할 권한이 있는지를 확인하기 위해 매번 데이터베이스를 조회하는 대신 JWT의 정보를 통해 권한을 빠르게 확인할 수 있습니다.

     

    HTTP 헤더를 통해 JWT 토큰을 보낼 때, 토큰 사이즈가 너무 커지는 것에 주의해야 하는데 몇몇 서버들은 8KB 이상의 헤더를 받을 수 없기 때문입니다. 만약 JWT 토큰에 사용자 권한정보와 같이 너무 많은 정보들을 포함하려고 한다면 Auth0 세밀한 권한 관리(Fine-Grained Authorization)과 같은 대체 솔루션을 고려해야 할 수 있습니다.

     

    토큰이 Authorization 헤더에 포함되어 전송된다면, 쿠키 기반 인증에서 발생할 수 있는 특정한 Cross-Origin Resource Sharing(CORS)문제들을 피할 수 있습니다. JWT 방식은 쿠키를 사용하지 않기 때문입니다. 하지만 JWT가 CORS 문제를 완전히 해결해주지는 않습니다.

    [!] CORS 정책

    CORS는 다른 출처의 리소스에 접근할 때 적용되는 웹 보안 정책입니다. 웹 애플리케이션은 다른 도메인으로부터의 자원 요청이 기본적으로 제한되어 있습니다. 이 정책은 보안상의 이유로 웹 페이지가 다른 도메인의 리소스를 요청하려 할 때 브라우저에서 제한을 걸어둡니다.

    JWT를 사용할 때, 토큰은 일반적으로 Authorization 헤더를 통해 전송됩니다. 이 방식은 쿠키 기반 인증과 달리 쿠키를 사용하지 않으므로, 쿠키와 관련된 CORS 이슈를 피할 수 있습니다. 그러나, 이는 JWT가 CORS 정책에 영향을 전혀 받지 않는다는 의미는 아닙니다. 서버는 여전히 적절한 CORS 헤더(Access-Control-Allow-Origin 등)를 설정하여 다른 출처의 요청을 허용해야 합니다. 따라서, JWT를 사용하는 경우에도, 서버와 클라이언트 모두에서 CORS 관련 설정을 고려해야 합니다.

     

    [!] CORS 관련한 예시

    예를 들어, 사용자가 mywebsite.com이라는 웹 사이트를 방문했다고 상상해봅시다. mywebsite.com의 웹 페이지에는 사용자의 최근 구매 내역을 보여주는 기능이 있습니다. 이 데이터는 datastore.com이라는 별도의 API 서버에서 가져옵니다.

    사용자가 mywebsite.com에서 자신의 최근 구매 내역을 확인하려고 버튼을 클릭합니다.그러면 mywebsite.com의 프론트엔드는 datastore.com의 API를 호출하여 구매 내역 정보를 요청합니다.여기서 문제가 발생합니다. 브라우저의 CORS 정책으로 인해 mywebsite.com이 datastore.com에 직접 요청하는 것을 제한합니다. 이는 사용자의 브라우저가 보안을 위해 다른 도메인(datastore.com)으로의 요청을 차단하기 때문입니다.
    이러한 상황에서 CORS 정책을 해결하려면, datastore.com 서버에서 적절한 CORS 헤더를 설정하여 mywebsite.com 도메인에서의 요청을 허용해야 합니다. 예를 들면, Access-Control-Allow-Origin: https://mywebsite.com 헤더를 추가해주면 됩니다.
    하지만 이렇게 설정하면, 쿠키와 같은 인증 방식을 사용할 때 추가적인 CORS 설정이 필요합니다. 반면 JWT 토큰을 사용하면, Authorization 헤더에 토큰을 포함시켜서 API를 호출할 수 있으며, 이 경우 쿠키를 전송하는 것과 관련된 CORS 문제를 회피할 수 있습니다.

     

    JWT는 어떻게 얻어지고 어떻게 사용되는가?

    1. Application 또는 Client가 인증 서버에게 인증을 요청합니다.
    2. 인증이 승인되면, 인증 서버는 액세스 토큰을 응용 프로그램에게 반환합니다.

    3. 응용 프로그램은 액세스 토큰을 사용하여 보호된 리소스(protected resourse:API)에 접근합니다.

     

    Signed token의 경우 토큰 내에 포함된 모든 정보는 외부에 노출되어 있음에 주의해야 합니다. 외부에서 토큰 값을 변경할 수는 없지만, 조회가 가능하기 때문에 토큰 내에 기밀 정보를 넣어서는 안됩니다.

     

    왜 JWT 토큰을 사용해야 하는가?

    Json Web Tokens의 이점에 대해 살펴보겠습니다. Simple Web Token(SWT), Security Assertion Markup Language Tokens (SAML)에 비해 어떤 점이 더 좋은 걸까요?

     

    JSON은 XML보다 간결하기 때문에, 더 작은 사이즈로 인코딩 할 수 있습니다. 따라서 JWT는 SAML보다 더 간결(compact)합니다. 이 특징 덕분에 HTML과 HTTP 환경에서 데이터를 주고받을 때 JWT를 사용하는 것이 더 유리해집니다.

     

    보안 측면에서 SWT는 HMAC 알고리즘을 사용하여 공유된 비밀키로만 서명될 수 있습니다. 그러나, JWT와 SAML 토큰은 X.509 인증서 형태의 공개/비공개 키 쌍을 사용할 수 있습니다. XML Digital Signature로 XML를 서명(sign)하는 것은 JSON에 비해 보안 문제가 발생할 가능성이 높습니다.

     

    대부분의 프로그래밍 언어에는 JSON 파서가 기본적으로 탑재되어 있지만 XML은 그렇지 않습니다. 이로 인해 SAML보다 JWT으로 작업하는 것이 더 쉽습니다.

     

    또한, JWT는 널리 사용되고 있기 때문에 다양한 플랫폼에서 JWT를 쉽게 처리할 수 있는 장점이 있습니다.

     

     

    References

    https://jwt.io/introduction

    반응형

    댓글