개발일지

JWT(Json Web Token) 본문

CS, WEB

JWT(Json Web Token)

lyjin 2022. 11. 20.
 

JWT(Json Web Token)

지난 포스트에서 토큰 기반 인증 시스템에 대해 알아보았다. 이번 포스트에서는 토큰 기반 인증 시스템의 구현체인 JWT(Json Web Token)에 대해 알아보고자 한다.

 

JWT란?

JSON Web Token(JWT)은 웹 표준(RFC 7519)으로서 두 개체 간에 정보를 안전하고 간결하게 전송하기 위한 방법을 정의한다. Json 포맷을 사용하여 자가수용적(self-contained) 방식으로 사용자 정보를 저장한다.

 

JWT는 다음과 같은 특성을 가진다.

  • 웹 표준으로서 C, Jave, Python, JavaScript 등 대부분의 주류 프로그래밍 언어에서 지원된다.
  • 자가수용적 (self-contained)이다. JWT는 토큰 정보, 전달할 정보, 검증됐다는 것을 증명해주는 서명 등의 모든 정보를 자체적으로 지니고 있다.
  • JWT는 자가수용적이므로 손쉽게 전달될 수 있다. 웹서버의 경우 HTTP 헤더에 넣어 전달할 수도 있고 URL의 파라미터로도 전달할 수 있다.
  • JWT는 회원을 인증해야 하거나, 두 개체 간 정보를 안전하게 교류하기 위해 사용될 수 있다.

 


JWT 구조

" . "을 구분자로 하는 문자열 형태로 헤더(Header), 페이로드(Payload), 서명(Signature)으로 구성된다.

HEADER.PAYLOAD.SIGNATURE

 

 

1.  헤더 (Header)

헤더는 두가지 정보를 포함하고 있다.

{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "Key ID"
}
  • alg: 서명 시 사용하는 해싱 알고리즘
  • typ: 토큰의 타입
  • kid: 서명 시 사용하는 키(public/private key)를 식별하는 값

위의 정보를 Base64Url 로 인코딩하면 헤더가 형성된다.

 

 

 

2.  정보 (Payload)

페이로드에는 토큰에 담을 정보가 들어있다. 여기에 담을 정보의 한 조각을 클레임 (claim) 이라 부르고 이는 name-value의 쌍으로 이루어져있다. 클레임에는 등록된 클레임, 공개 클레임, 비공개 클레임 세가지 유형이 있다.

 

등록된 클레임 (registered claim)

서비스에서 필요한 정보들이 아닌 토큰에 대한 정보들을 담기위한 이미 정해진 이름의 클레임들이다. 모두 옵셔널이다.

  • iss: 토큰 발급자
  • sub: 토큰 제목
  • aud: 토큰 대상자
  • exp: 토큰 만료 기간. NumericDate 형식
  • nbf: Not Before를 의미하며 토큰의 활성 날짜와 비슷한 개념으로 이 날짜가 지나기 전까지는 토큰이 처리되지 않는다. NumericDate 형식
  • iat: 토큰 발급 시간. 이 값으로 토큰의 age를 판단할 수 있다.
  • jti: jwt 고유 식별자

 

공개 클레임 (public claim)

충돌이 방지된 이름을 가지고 있어야 한다. IANA JSON 웹 토큰 레지스트리에 정의하거나 충돌 방지 네임스페이스를 포함하는 URI로 정의해야한다.

 

 

비공개 클레임 (private claim)

양 측간에(보통 클라이언트/서버) 협의하에 사용되는 클레임 이름들이다.

 

페이로드 예시

{
    "iss": "velopert.com",       // registered
    "exp": "1485270000000",         // registered
    "https://velopert.com/jwt_claims/is_admin": true, // public
    "userId": "11028373727102",  // private
    "username": "velopert"          // private 
}

 

헤더와 마찬가지로Base64Url로 인코딩하면 페이로드가 형성된다.

 

3.  서명 (Signature)

 

헤더의 인코딩 값과 페이로드의 인코딩 값을 합친 후 주어진 Secret-Key로 해싱하여 생성된다. 예를 들어 HMAC SHA256 알고리즘을 사용하려는 경우 서명은 다음과 같은 방식으로 생성된다.

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

 

생성한 해시값을 Base64Url 로 인코딩하면 서명이 형성된다. 문자열이 아닌 hex → Base64Url 인코딩 해야한다.

 

이렇게 구한 값들을 " . " 중간자로 합쳐주면 다음과 같은 토큰이 완성된다.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ2ZWxvcGVydC5jb20iLCJleHAiOiIxNDg1MjcwMDAwMDAwIiwia
HR0cHM6Ly92ZWxvcGVydC5jb20vand0X2NsYWltcy9pc19hZG1pbiI6dHJ1ZSwidXNlcklkIjoiMTEwMjgzNzM3MjcxMDIiLC
J1c2VybmFtZSI6InZlbG9wZXJ0In0.WE5fMufM0NDSVGJ8cAolXGkyB5RmYwCto1pQwDIqo2w

 

jwt.io 디버거으로 JWT를 디코딩하고 검증 및 생성해볼 수 있다.

 


JWT 사용하기

동작 방식

헤더와 페이로드는 인코딩될 뿐 따로 암호화 되지 않기 때문에 누구나 디코딩하여 확인할 수 있다. 따라서 페이로드에는 비밀번호와 같은 민감한 정보를 포함하면 안된다. 그러나 서명은 해싱할 때 사용한 Secret-Key를 알지 못하면 복호화 할 수 없다. 검증 시 이 Secret-Key를 사용해 검증하므로 Secret-key는 절대 노출되선 안된다.

  1. 사용자가 아이디와 비밀번호로 로그인을 한다.
  2. 서버 측에서는 해당 계정 정보를 검증하고 토큰을 생성한다.
    2-1. 사용자 고유 식별 값(사용자 ID)을 기타 정보와 함께 페이로드에 넣는다.
    2-2. JWT 유효기간 등을 설정하고 Secret-Key로 Access Token(JWT)을 발급한다.
  3. 클라이언트는 토큰을 저장하고 있다가 서버에 요청할 때마다 해당 토큰을 함께 전송한다.
  4. 서버에서는 토큰을 Secret-Key로 검증한다. (2-2 발급 시 key와 동일)
  5. 토큰 검증되면 페이로드를 디코딩하여 사용자 ID에 맞는 데이터를 가져온다.

 

장점

  • 세션 방식과 달리 토큰 그 자체로 검증 가능하기 때문에 별도의 저장 공간이 필요없다.
  • 헤더와 페이로드를 가지고 서명을 생성하므로 데이터 위변조를 막을 수 있다.
  • 토큰이 발급되면 유효기간이 만료될 때까지 계속해서 사용할 수 있다.

 

단점

  • 쿠키/세션에 비해 JWT 토큰 길이가 길어 인증 요청이 많아질수록 네트워크 부하가 심해진다.
  • JWT는 한번 발급되면 유효기간이 만료될 때까지 유효하기 때문에 탈취당하면 대처하기 어렵다.
  • 페이로드는 디코딩하면 데이터를 알 수 있기 때문에 중요한 데이터는 넣지 말아야한다.

 


Refresh Token

앞서 살펴본듯이 Access Token(JWT) 인증 방식은 탈취당할 경우 대처하기 어렵다는 문제가 있다. 보안성을 높이기 위해 Access Token 유효기간을 짧게 설정할 수도 있지만 사용자 입장에서는 그만큼 로그인을 자주 해줘야하기 때문에 번거롭다.


이에 대한 대책으로 리프레시 토큰(Refresh Token)이라는 추가적인 토큰을 활용할 수 있다. 이 리프레쉬 토큰은 사용자 인증이 아닌 만료된 액세스 토큰을 재발급하는 용도로만 사용한다.

  • 액세스 토큰의 유효 기간은 짧게, 리프레시 토큰의 유효 기간은 길게 설정한다.
  • 사용자 로그인 시 서버는 액세스 토큰과 리프레시 토큰 둘 다 발급하여 응답한다.
  • 클라이언트는 액세스 토큰을 사용하다가 만료 시에 액세스 토큰 재발급을 요청한다. 이때 리프레시 토큰을 포함하여 함께 전송한다.
  • 서버에서는 리프레시 토큰을 검증을 하고, 유효한 토큰일 경우 액세스 토큰을 재생성하여 전송한다.

 

액세스 토큰을 탈취 당하면 정보가 유출되는 것은 동일하지만, 액세스 토큰에 대한 유효기간을 짧게 설정할 수 있기에 비교적 안전하다.
리프레시 토큰도 브라우저에 노출되기 때문에 서버 측에서 암호화하여 전송해주는 것이 보안상 좋으며, JWT와 동일하게 만료 됐을 경우 재발급해줘야한다.

 

 

레퍼런스

🔗 https://jwt.io/introduction
🔗 https://velopert.com/2389
🔗 https://velog.io/@kingth/%EC%84%9C%EB%B2%84-%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D%EC%84%B8%EC%85%98%EC%BF%A0%ED%82%A4-%ED%86%A0%ED%81%B0

'CS, WEB' 카테고리의 다른 글

로드밸런싱이란?  (0) 2024.02.07
OAuth 2.0 알아보기  (0) 2022.12.12
PASETO (Platform-Agnostic SEcurity TOkens)  (0) 2022.11.25
토큰(Token) 기반 인증  (0) 2022.11.20
쿠키(Cookie)와 세션(Session)  (0) 2022.11.20