JWT

leedong617·2024년 10월 14일
post-thumbnail

JWT(JSON Web Token)는 JSON 객체를 이용해 정보를 안전하게 전송하기 위한 토큰 기반 인증 방식임. 토큰은 사용자의 인증 상태를 유지하고, 자원에 대한 접근 권한을 부여하기 위해 사용됨. JWT는 세션 상태를 서버에 유지할 필요 없이, 클라이언트가 보관하는 토큰만으로 인증을 처리할 수 있는 특징 덕분에 무상태(stateless) 인증 방식으로 매우 인기가 있음.

JWT의 구조

JWT는 3개의 부분으로 구성되며, 각각 Base64Url로 인코딩됨. 이 3개의 부분은 점(.)으로 구분되며, 일반적으로 다음과 같은 형식을 가짐.

<헤더(Header)>. <페이로드(Payload)>. <서명(Signature)>

헤더(Header)

헤더는 JWT의 타입과 서명 알고리즘을 지정하는 정보가 담겨 있음. 일반적으로 알고리즘은 HS256(HMAC SHA-256)이나 RS256(RSA SHA-256)과 같은 방식으로 사용됨.

예시:

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg: 서명 알고리즘을 지정하며, 서명(Signature) 및 토큰 검증에 사용함. 예: HS256
  • typ: 토큰의 타입을 지정함. JWT가 기본 타입임.

페이로드(Payload)

페이로드는 JWT에 포함할 실제 정보가 담긴 부분으로, 클레임(Claim)이라는 형태로 데이터를 표현함. JSON(Key/Value) 형태의 한 쌍으로 이루어져 있음. 중요한 정보(예: 비밀번호)는 페이로드에 포함하지 않아야 함. 클레임 의 종류는 다음과 같이 크게 세 분류로 나뉘어져있음

  • 등록된 (registered) 클레임
  • 공개 (public) 클레임
  • 비공개 (private) 클레임

등록된 클레임(Registered Claim)

등록된 클레임들은 서비스에서 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기위하여 이름이 이미 정해진 클레임들임. 등록된 클레임의 사용은 모두 선택적임.

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

{
  "iss": "leedong617",
  "exp": 1516239022,
}

공개 클레임(Public Claim)

공개 클레임은 사용자 정의 클레임으로, 공개용 정보를 위해 사용된다. 충돌 방지를 위해 URI 포맷을 이용함.

{
  "https://velog.io/@leedong617/post/jwt": true,
}

비공개 클레임(Private Claim)

사용자 정의 클레임으로, 클라이언트와 서버가 협의하에 임의로 지정한 정보를 저장해서 사용함.
공개 클레임과는 달리 이름이 중복되어 충돌이 될 수 있으니 사용할때에 유의해야함.

{
  "username": "leedong"
}

예시:

{
  "iss": "leedong617",
  "exp": 1516239022,
  "https://velog.io/@leedong617/post/jwt": true,
  "username": "leedong"
}

서명(Signature)

서명은 헤더와 페이로드를 조합한 후, 서명 알고리즘과 비밀키를 사용하여 생성됨. 서명은 JWT의 무결성과 변조 방지를 위해 존재함. 클라이언트와 서버 간 전송 중 토큰이 변경되지 않았음을 보장함.

서명 생성 과정은 다음과 같음

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret) //your-256-bit-secret부분에 정해진 암호나 자신만의 암호를 넣으면 됨.

JWT의 최종 결과물은 다음과 같이 생성됨

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJpc3MiOiJsZWVkb25nNjE3IiwiZXhwIjoxNTE2MjM5MDIyLCJodHRwczovL3ZlbG9nLmlvL0BsZWVkb25nNjE3L3Bvc3Qvand0Ijp0cnVlLCJ1c2VybmFtZSI6ImxlZWRvbmcifQ
.
Zd0JrO7x5ddU3R3QfzyWZtr_QBytlc__eIPqTnfADBo

JWT의 특징

자체적으로 상태를 유지

JWT는 서버가 세션을 유지할 필요 없이, 자체적으로 상태를 유지함. JWT 자체에 필요한 정보가 포함되어 있기 때문에, 서버가 별도의 상태 관리 없이 무상태(stateless)로 인증 처리가 가능함.

정보 전송의 안전성

JWT는 서명(Signature)을 통해 토큰이 변조되지 않았음을 검증할 수 있음. 따라서 토큰이 변경되면 서버는 이를 감지하고 토큰을 무효화할 수 있음.
암호화가 아닌 서명을 통해 보안이 유지되므로, 기밀 정보는 페이로드(Payload)에 포함시키지 않음이 중요함.

토큰 기반 인증

JWT는 클라이언트가 발급받은 토큰을 HTTP 요청 헤더에 포함하여 서버에 요청을 보내는 토큰 기반 인증 방식임. 일반적으로 Authorization: Bearer <token> 형식으로 전송함.

플랫폼 독립적

JWT는 JSON 형식을 사용하므로, 플랫폼에 상관없이 대부분의 언어와 호환되어 다양한 클라이언트에서 사용할 수 있음.

JWT의 동작 과정

토큰 발급

  1. 클라이언트가 서버에 인증 요청을 보냄. (예: 사용자명/비밀번호)

  2. 서버는 요청을 확인하고, 사용자가 유효하다면 JWT를 생성하여 클라이언트에 반환함.

  3. 클라이언트는 서버로부터 받은 JWT를 저장함. (예: Local Storage, 쿠키)

토큰 사용

  1. 클라이언트는 서버에 요청을 보낼 때, HTTP 헤더에 JWT를 포함하여 서버에 인증된 요청을 보냄.

  2. 서버는 헤더에 있는 JWT를 검증하고, 유효한 토큰일 경우 요청을 처리하고 필요한 자원에 접근하도록 허용함.

  3. 만료된 토큰이거나, 서명이 유효하지 않으면 서버는 요청을 거부함.

JWT토큰에 대한 유효성 검증

Header 검증

  • 서버는 토큰의 헤더 부분을 base64로 디코딩하여 JSON 객체로 변환함.
  • 변환된 JSON 객체에서 알고리즘 및 토큰 종류를 확인함. (예 : HS256, JWT)
  • 알고리즘은 서버에 지정된 알고리즘과 일치해야 함.

Payload 검증

  • 서버는 토큰의 페이로드 부분을 base64로 디코딩하여 JSON객체로 변환함.
  • 변환된 JSON 객체에서 발급 시간, 만료 시간, 사용자 정보 등을 확인함.
  • 만료 시간이 현재 시간보다 이전이면 토큰이 만료된 것으로 간주함.
  • 사용자 정보는 서버에 저장된 사용자 정보와 일치해야 함.

Signature 검증

  • 서버는 토큰의 시그니처 부분을 base64로 디코딩하여 바이트 배열로 변환함.
  • 헤더에서 확인한 알고리즘을 사용하여 서버의 비밀 키와 페이로드를 기반으로 새로운 서명을 생성함.
  • 생성된 새로운 서명과 클라이언트에게 받은 토큰의 서명을 비교함.
  • 두 시그니처가 일치해야 유효성 검증이 이루어짐.

JWT의 장점

무상태(stateless)

  • 서버가 클라이언트의 세션 상태를 유지하지 않으므로, 확장성(Scalability)이 뛰어남. 여러 서버 간에 인증 상태를 공유할 필요가 없어, 부하 분산이 용이함.

유연한 권한 부여

  • JWT의 페이로드에 포함된 권한 정보를 바탕으로, 클라이언트에게 다양한 자원에 대한 접근 권한을 유연하게 부여할 수 있음.

간편한 관리

  • JWT는 표준화된 JSON 형식을 사용하여 사용자 정보와 권한 정보를 표현하므로, 다양한 플랫폼과 언어에서 쉽게 처리할 수 있음.

보안성

  • JWT의 서명으로 인해 토큰의 위변조 방지가 가능함.
  • HTTPS를 사용하여 토큰 전송 시 중간자 공격으로부터 보호할 수 있음.

JWT의 단점

토큰 만료 관리

  • JWT는 서버에서 만료 전까지는 토큰을 임의로 폐기할 수 없음. 사용자가 로그아웃하거나 권한을 변경했을 때, 토큰이 만료될 때까지 효력이 유지되므로 관리가 어렵다는 단점이 있음.
  • 이를 해결하기 위해 리프레시 토큰(Refresh Token)을 사용하거나, 서버에서 블랙리스트를 관리하는 방식이 있음.

기밀 정보 노출 위험

  • JWT는 페이로드가 암호화되지 않음. 따라서, 민감한 정보를 포함시키면 정보 노출 위험이 있음. 중요한 정보는 페이로드에 포함하지 않아야 함.

토큰 크기

  • JWT는 클라이언트가 직접 보관하고 매 요청마다 전송되므로, 토큰 크기가 커지면 네트워크 대역폭에 부담이 될 수 있음. 특히, 페이로드에 많은 정보를 담을 경우 더욱 큼.

재발급 복잡성

  • 만료된 JWT는 서버에서 새롭게 재발급해야 하므로, 이를 관리하기 위한 추가적인 로직이 필요함. 특히, 보안이 중요한 서비스에서는 리프레시 토큰을 통해 인증을 갱신해야 함.

토큰 인증 신뢰성을 가지는 이유

서버는 클라이언트로부터 JWT를 받으면 header, payload를 서버의 key값을 이용해 signature를 다시 만들고 이를 비교하여 일치했을 경우 통과시킴.

JWT토큰은 A(header) + B(payload) + C(signature)로 구성되어 있음.

JWT => A + B + C

만약 누군가 토큰의 내용이 담겨져 있는 payload를 수정하는경우 B'로 표기.

임의의 유저에 의해 수정된 JWT => A + B' + C

  • 서버가 유저로부터 A+B+C로 구성된 토큰을 받았을때 서버는 A+B로 C를 다시 만든 후 요청때 받은 토큰의 C와 서버가 만든 C를 비교후 일치한다면 통과시킴.

서버에 비밀키가 안전하게 보관되어 있으므로 위조가 되지 않았다면 두 서명은 일치하기 때문임.

  • 만약 서버가 유저로부터 A+B'+C로 구성된 변조 토큰을 받았을때 서버는 A+B'로 C'를(변조된 서명) 만듬. 이후, 서버는 요청때 받은 토큰의 C와 서버가 만든 C'를 비교함.

  • 이때 서버는 변조된 payload인 B'를 받았으므로 A+B'로 새로만든 서명은 C가 아닌 C'가 나올수밖에 없음. B'로 서명을 새로 만들었기 때문임.

따라서 C와 C'는 다르므로 이때 받은 요청은 서버가 신뢰할수 없다고 판단함.

JWT은 signature(인증, 서명)가 목적

JWT는 Base64로 암호화를 하기 때문에 디버거를 사용해서 인코딩된 JWT를 복호화하기 매우 쉬움. 복호화 하면 사용자의 데이터를 담은 Payload 부분이 그대로 노출되어 버림. 그래서 페이로드에는 비밀번호와 같은 민감한 정보는 넣지 말아야 함. 토큰의 진짜 목적은 '정보 보호'가 아닌, 해당 토큰이 신뢰 가능한 토큰인지 확인하는것 즉, '위조방지'가 목적임. 바로 위에서 소개했듯이, 시그니처에 사용된 비밀키가 노출되지 않는이상 데이터를 위조해도 시그니처 부분에서 바로 걸러지기 때문.

JWT 사용 사례

RESTful API 인증

JWT는 무상태(stateless) 인증 방식이기 때문에, 서버가 상태를 저장하지 않는 RESTful API에서 매우 적합함. 클라이언트는 인증된 JWT를 전송하여 서버에 요청을 보내고, 서버는 이 토큰을 기반으로 인증 및 권한을 확인함.

싱글 페이지 애플리케이션(SPA)

SPA에서는 클라이언트가 서버로부터 한번 인증을 받은 후, JWT를 통해 페이지를 새로고침하지 않고도 인증 상태를 유지할 수 있음. 예를 들어 React나 Angular 같은 프론트엔드 프레임워크에서 자주 사용됨.

마이크로서비스 아키텍처(MSA)

JWT는 토큰 기반으로 인증을 처리하기 때문에, 마이크로서비스 간 통신에서 유용하게 사용됨. 각 마이크로서비스가 독립적으로 인증을 관리할 필요 없이, JWT를 사용해 인증 상태를 공유하고 서비스 간 인증을 수행할 수 있음.

JWT Access Token / Refresh Token

Access Token

사용자의 권한과 인증 상태를 담고 있어, 자원에 대한 접근을 허용하기 위한 토큰.
주로 짧은 유효 기간을 가지며, 사용자가 서버에 자원 요청을 보낼 때마다 인증 정보로 사용됨.

Access Token의 특징

  • 짧은 유효 기간: Access Token은 수 분에서 수 시간 정도의 짧은 유효 기간을 가짐. 만료되면 더 이상 사용할 수 없고, 새로 발급받아야 함.

  • 클라이언트에 저장: Access Token은 주로 클라이언트 측에서 저장되고, 자원에 접근할 때마다 전송됨. 일반적으로 Authorization 헤더에 Bearer <token> 형식으로 포함되어 전송됨.

  • 무상태: 서버는 Access Token 자체만으로 인증을 처리하므로, 서버 측에서 세션을 유지할 필요가 없음. 이로 인해 서버 확장성이 높아지고, 무상태(stateless) 특성을 가짐.

  • 사용자의 권한과 정보 포함: Access Token에는 사용자의 역할이나 권한과 같은 인증 관련 정보가 포함될 수 있음. 이 정보는 서버가 요청을 처리할 때 참고하여 접근 제어를 수행함.

Access Token의 사용 과정

  1. 사용자가 인증에 성공하면, 서버는 Access Token을 발급하여 클라이언트에 전달함.

  2. 클라이언트는 이후 서버에 자원을 요청할 때마다 Access Token을 포함하여 인증을 받음.

  3. 서버는 토큰을 검증하고, 유효한 경우 사용자의 요청을 처리하여 자원에 접근을 허용함.

  4. Access Token이 만료되면, 클라이언트는 새로운 Access Token을 발급받기 위해 Refresh Token을 사용함.

Refresh Token

Access Token을 재발급받기 위한 장기 토큰(긴 유효기간)으로, Access Token이 만료되었을 때 사용하여 새로운 Access Token을 서버에서 요청할 수 있음.

Refresh Token의 특징

  • 긴 유효 기간: Refresh Token은 수 일에서 수 주 또는 수 개월 정도의 긴 유효 기간을 가짐. Access Token과 달리, 상대적으로 긴 시간 동안 사용할 수 있음.

  • 서버 측에서의 보관과 검증: Refresh Token은 보안성을 위해 주로 서버 측에서 보관하고 관리하는 것이 일반적임. 서버에서 발급된 Refresh Token 목록을 유지하거나, 데이터베이스(DB)에 저장하여 유효성을 검증할 수 있음.

  • 재발급 용도: Refresh Token은 Access Token이 만료되었을 때만 사용되며, 사용자에게 새로운 Access Token을 부여하기 위한 목적으로 사용됨.

  • 보안성: Refresh Token은 클라이언트의 기밀 정보로 간주되며, 보안성을 높이기 위해 세션 하이재킹 방지와 같은 보안 메커니즘을 적용함.

Refresh Token 사용 과정

  1. 사용자가 Access Token을 사용하여 서버에 자원 요청을 보냄.

  2. Access Token이 만료되었을 경우, 클라이언트는 Refresh Token을 이용해 새로운 Access Token을 요청함.

  3. 서버는 같이 보내진 Refresh Token을 쿠키나 DB에 저장된 값과 비교하여 Refresh Token의 유효성을 검증하고, 유효하다면 새로운 Access Token을 발급하여 클라이언트에 반환함.

  4. 클라이언트는 새로운 Access Token을 받아, 다시 자원 요청에 사용할 수 있게 됨.

  5. Refresh Token 자체가 만료되거나 무효화되면, 클라이언트는 다시 인증 절차를 통해 새로운 Refresh Token을 발급받아야 함.

  6. 만일 사용자가 로그아웃할 경우 Refresh Token을 삭제하여 재사용이 불가능하도록 처리함.

Access Token과 Refresh Token의 차이점

특징Access TokenRefresh Token
용도자원 요청 시 인증용으로 사용Access Token이 만료되었을 때 재발급용으로 사용
유효 기간짧음 (수 분 ~ 수 시간)긺 (수 일 ~ 수 개월)
저장 위치클라이언트 측서버 측에 저장하는 것이 보안상 더 안전함
사용 빈도모든 요청에 대해 사용Access Token이 만료될 때만 사용
보안 우려짧은 유효 기간으로 인해 상대적으로 안전기밀 정보로 간주, 보안 관리 필요
재발급 기능불필요함 (자체적으로 Access Token을 재발급 불가)필요함 (새로운 Access Token을 발급)

Access Token과 Refresh Token의 관리

Access Token 관리

Access Token은 짧은 유효 기간을 가지므로, 클라이언트에서 보관하고 요청 시마다 전송되므로 세션 하이재킹 또는 중간자 공격에 취약할 수 있음. 이를 방지하기 위해 HTTPS를 사용하여 토큰이 안전하게 전송되도록 해야 함. Access Token은 Local Storage, 세션 스토리지, 브라우저 메모리, HTTP 쿠키 에 저장 될 수 있음.

Refresh Token 관리

Refresh Token은 보안상의 이유로 서버 측에서 보관하는 것이 권장됨. 이 경우, Refresh Token을 발급할 때 데이터베이스에 저장하거나, 블랙리스트 관리 등을 통해 유효성을 관리할 수 있음. Refresh Token은 보안성 때문에 클라이언트에 저장할 경우, HTTP-Only 쿠키에 저장하여 자바스크립트로 접근하지 못하도록 해야 함.

Refresh Token Rotation (RTR)

Refresh Token을 사용할 때마다 새로운 Refresh Token을 발급하고, 기존의 Refresh Token을 무효화함. 이 방법은 Refresh Token이 도난당하더라도 한 번만 사용할 수 있기 때문에 재사용 위험을 방지함.

보안 강화 방법

  • HTTPS 사용: Access Token과 Refresh Token을 전송할 때는 반드시 HTTPS를 사용하여 전송 중 암호화함.

  • HTTP-Only 및 Secure 쿠키: 클라이언트에 저장할 경우 HTTP-Only 쿠키로 설정하여 자바스크립트에서 접근하지 못하도록 하고, Secure 속성을 사용하여 HTTPS 연결에서만 전송되도록 함.

  • 만료 시간: Access Token의 만료 시간을 짧게 유지하고, Refresh Token의 유효 기간을 관리하여 보안성을 높임.

  • 블랙리스트: 로그아웃 시 Refresh Token을 블랙리스트에 추가하여, 더 이상 사용되지 않도록 함.

  • IP와 사용자 에이전트 추적: Refresh Token 사용 시, IP 주소와 사용자 에이전트 정보를 추적하여 의심스러운 활동을 탐지하고, 토큰을 무효화함.

결론

  • JWT는 무상태(stateless) 인증 방식이 필요한 애플리케이션에서 사용자 인증과 권한 부여를 효과적으로 처리할 수 있는 도구임.
  • 특히, RESTful API, SPA, 마이크로서비스 아키텍처와 같이 확장성이 중요한 시스템에서 자주 사용됨.
  • 다만, 기밀 정보의 보관 및 토큰 관리에 신경 써야 하며, 리프레시 토큰과 블랙리스트 등을 통해 보안성을 보완하는 것이 좋음.
  • JWT는 다양한 플랫폼과 호환되며, JSON 형식을 사용하여 유연하고 직관적인 인증 시스템을 제공함으로써, 현대 애플리케이션의 중요한 보안 메커니즘 중 하나로 자리잡고 있음.
profile
웹개발자 취업 준비생

0개의 댓글