ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • json web token (JWT)
    Security 2023. 3. 24. 20:49
    728x90
    반응형

     


    개요

    jwtjson web token 의 약자로, 표준적으로 통용되는 인증 방식 중 하나입니다. 

    jwt 의 이름은 jsonweb 을 포함하는데요.

    json 이 포함된 이유는 전자 서명을 생성 과정에서 json 형식을 사용되기 때문이고,
    web 이라는 표현 또한 web 생태계 jwt 가 주요하게 활용되는 영역임을 보여줍니다.

     

    아래 내용에서 jwt 이라는 전자 서명 방식이 정확히 무엇이며, 어떻게 생성 및 활용되는지와 관련 예제를 설명해보겠습니다. 


    구성

    jwt 토큰헤더(header), 페이로드(payload), 시그니처(signature) 세가지 파트로 구성됩니다.

     

    Header

    헤더는 두가지 정보를 포함합니다. 

    • typ (타입)
    • alg (사용된 해시 알고리즘)
    {
     "typ" : "JWT",
     "alg" : "RSA"
    }

    typJWT 가 고정적으로 사용됩니다. 

    algHMAC SHA256 혹은 RSA 가 두루 사용된다고 합니다. (추후에 해시 알고리즘에 대한 상세한 내용을 추가적으로 기록하고자 합니다.)

     

    헤더는 장황한 데이터를 표현하기 보다는 위의 예시처럼 두가지 정보를 간단히 표현합니다. 

    Payload

    페이로드는 jwt 전자서명의 메타데이터를 표현합니다. 

    • 서명의 발행자가 누구인지 (iss),
    • 서명의 발행일이 언제인지 (iat),
    • 서명의 만료일이 언제인지 (exp)

    등의 정보를 담습니다. 

    이러한 정보를 클레임(Claim) 이라고 부르며, 클레임들은 세부적으로 분류됩니다.

     

    1. registered claim
    2. custom claim 

    registered claim 는 예약된 형식이 존재합니다. 

     

    • iss (issuer): Issuer of the JWT
    • sub (subject): Subject of the JWT (the user)
    • aud (audience): Recipient for which the JWT is intended
    • exp (expiration time): Time after which the JWT expires
    • nbf (not before time): Time before which the JWT must not be accepted for processing
    • iat (issued at time): Time at which the JWT was issued; can be used to determine age of the JWT
    • jti (JWT ID): Unique identifier; can be used to prevent the JWT from being replayed (allows a token to be used only once)

     

    위 7개의 명세가 존재하며 필요에 따라 jwt 의 payload 에 추가하면 됩니다. 

     

    custom claim 은 예약된 형식의 클레임이 아닌 인증 절차의 특성에 따라 커스텀하게 추가할 수 있는 클레임입니다. 

    jwt 형식의 유연함을 제공합니다. 

    인증을 요청하는 사용자의 이름을 identifier 과 같은 형식으로 추가하곤 합니다. 

     

    Payload 의 예시를 아래와 같습니다. 

    {
    	"iss": "westlife0615.com", // issuer (발행자)
    	"iat": 1679651333029, // Fri Mar 24 2023 18:48:53 GMT+0900 (발행 일시)
    	"exp": 1682899200000, // Mon May 01 2023 09:00:00 GMT+0900 (만료 일시)
    	"identifier": "guest1"
    }

     

    signature

    signaturejwt 의 마지막 구성요소입니다. 

    시그니처헤더와 페이로드를 암호화한 결과입니다. 

    구체적으로는 base64 로 인코딩된 header 와 payload 를 해시 암호화한 결과가 signature 에 해당합니다. 

    암호화 과정에서 secret 이라는 암화화 키를 사용하는데, 

    jwt 생성과 인증을 하는 과정에서 secret 이 사용됩니다. 

     

    예를 들어보겠습니다. 

    var jwtHeader = {
    	"alg": "HS256",
    	"typ": "JWT"
    };
    
    var jwtHeaderString = JSON.stringify(jwtHeader);
    
    var encodedHeader = btoa(jwtHeaderString)
    console.log(encodedHeader)
    
    // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

     

    HS256 해시 알고리즘을 사용하는 경우, jwt header 는 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 입니다. 

     

    var jwtPayload = {
      "iss": "westlife0615",
      "name": "guest1",
      "iat": 1516239022
    };
    
    var jwtPayloadString = JSON.stringify(jwtPayload);
    
    var encodedPayload = btoa(jwtPayloadString)
    console.log(encodedPayload)
    
    // eyJpc3MiOiJ3ZXN0bGlmZTA2MTUiLCJuYW1lIjoiZ3Vlc3QxIiwiaWF0IjoxNTE2MjM5MDIyfQ

     

    registered claim 인 issuer 가 westlife0615, issued at 이 1516239022 이고 

    custom claim 인 name 이 guest1

    인 경우에 jwt payload 는 eyJpc3MiOiJ3ZXN0bGlmZTA2MTUiLCJuYW1lIjoiZ3Vlc3QxIiwiaWF0IjoxNTE2MjM5MDIyfQ 입니다. 

     

     

    마지막으로 base64 로 인코딩된 header 와 payload"abc" 라는 secret 으로 암호화를 하면 

    uQvtl7UyRN-rVstKE5qRTnEIMTaWQHXIwG0O_-XW288 으로 인코딩됩니다. 

     

    그럼 결과적으로 jwt 는 

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
    eyJpc3MiOiJ3ZXN0bGlmZTA2MTUiLCJuYW1lIjoiZ3Vlc3QxIiwiaWF0IjoxNTE2MjM5MDIyfQ.
    uQvtl7UyRN-rVstKE5qRTnEIMTaWQHXIwG0O_-XW288

    와 같이 표현됩니다. 

     

    signature 는 secret 이라는 암호화를 위한 key 가 중요한 요소입니다. 

    보안적으로 중요한 요소이며, token 을 디코딩하는 관점에서 . 으로 구분되는 header 와 payload 를 secret 으로 암호화했을 경우, 

    암호화된 signature 과 jwt 의 signature 가 동일해야한 인증을 성공적으로 수행할 수 있습니다. 

     

    base64

    base64는 jwt 토큰을 생성하는 과정에서 사용되는 인코딩 방식이라서 간단한 설명을 하겠습니다.

    base64는 아스키코드, 유니코드와 같이 텍스트를 인코딩하는 표준 방식 중 하나입니다.
    아스키코드가 7비트의 범주에서 텍스트를 변환하는데에 반해 base64 는 6비트 단위로 텍스트를 변환합니다.
    base64 는 6비트 단위로 인코딩이 진행되기 때문에 필연적으로 2bit의 패딩이 발생합니다.
    즉, 데이터의 저장, 전송 측면에서 낭비가 발생하는거죠.
    그럼에도 불구라고 base64 방식의 인코딩이 사용되는 이유는 무엇일까요?

    base64는 6개의 비트를 사용하며 64개의 문자을 표현합니다.
    취급가능한 문자들은 a-z, A-Z, 0-9, +, / 로 총 64개입니다.
    시스템 별로 1번 비트인 significant bit 와 개행 문자의 처리 방식이 상이하다고 합니다.
    이러한 호환성의 문제를 개선하고자 base64 인코딩이 고안되었습니다.
    여러 환경에서 안정적으로 처리가능한 문자를 기반으로 텍스트를 인코딩한다고 생각하면 될거 같습니다.
    저장&전송 측면의 낭비가 있더라도 안정적으로 정보를 처리할 수 있죠.

    하지만 실제로 jwt 토큰 생성 시에는 base64url 방식의 인코딩이 사용됩니다.
    그 이유는 웹 환경에서 url의 포맷에서 /, = 등의 특수문자가 유의미하게 활용되는데,
    base64 의 변환 문자와 url 형식의 호환이 안되는 문제가 생깁니다.
    그래서 몇개의 특수문자가 변경된 인코딩방식이 base64url 방식이며 이 방식이 jwt 토큰 생성 시에 활용됩니다.

    반응형

    'Security' 카테고리의 다른 글

    CA (Certificate Authority) 알아보기  (2) 2023.11.28
    RSA 암호화 수학적 원리 이해하기  (0) 2023.09.24
Designed by Tistory.