JWT

Geon Lee·2024년 7월 3일

JWT

JWTJSON Web Token의 약자로 Web 환경에서의 안전한 정보 전송을 위한 개방형 표준으로 JSON 객체 형태로 정의된다.

개발자가 정의한 JWT가 사용되는 경우는 다음과 같다.

  • 권한 부여
    • 가장 일반적인 경우로 사용자가 로그인할 때 권한을 부여하는 목적으로 사용된다.
    • 로그인 이후 각 요청에 JWT가 포함되어 해당 토큰으로 허용된 경로, 서비스 및 리소스에 접근할 수 있다.
  • 정보 교환
    • JWT는 당사자 간에 안전한 정보 전달을 위해서도 사용된다.
    • JWT는 서명된 키로 사용되기 때문에 사용자 인증에 용이하다.
    • Header와 Payload를 사용하여 변조 여부도 확인 가능하다.

JWT 는 특히 사용자 인증 기능에 가장 많이 사용된다. 그래서 가장 많이 사용되는 인증 방식들을 한번 비교 해보았다.

  • 장점
    • 인증 테스트를 할 경우 및 간단한 앱을 만들때 빠르게 설계할 수 있음
  • 단점
    • 보안에 매우 취약하여 해킹을 당하는 일이 빈번함
    • 용량 제한이 있어 많은 정보를 담을 수 없다. (용량이 커질수록 네트워크 부하가 심해져 성능 저하 문제가 발생할 수 있음)

Session/Cookie

  • 장점
    • 매개가 되는 Cookie는 유의미한 값을 갖지 않음
      • CookieSession Storage에서 가져온 Session ID를 담는 역할
      • 따라서, 계정정보를 담아 인증을 거치는 것보다 안전함
    • 각 사용자마다 고유한 Session ID가 발급되기 때문에 요청이 들어올 때마다 회원정보를 확인할 필요가 없음
  • 단점
    • 세션 하이재킹 공격이 발생할 수 있음
      1. A 사용자의 HTTP 요청을 B 사용자(해커)가 가로챔
      2. B 사용자는 가로챈 HTTP 요청을 이용해 내부의 Cookie를 탈취함
      3. B 사용자가 탈취한 Cookie를 이용해 서버에 HTTP 요청을 보냄
      4. 서버는 B 사용자를 A 사용자로 오인하여 정보를 잘못 보냄
    • Session Storage를 서버에 구축해야 하기 때문에 사용자가 많아지는 경우 서버 부하가 발생할 수 있음
CookieSession/CookieJWT
보안취약강함강함
서버 부하낮음높음낮음
네트워크 부하높음낮음높음

구조

JWT는 JSON 정보가 Base64Url로 인코딩된 형태가 점(.)으로 구분된 세 부분으로 구성되며, 각 부분은 다음과 같다.

  • Header
  • Payload
  • Signature

세 부분이 각각 인코딩되어서 다음과 같은 형태를 띤다.

xxxxx.yyyyy.zzzzz

Header는 일반적으로 해시 알고리즘(HMAC SHA256 또는 RSA 등)과 토큰 유형을 정의한다.

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

Payload

Payload는 전달하고자 하는 데이터에 관한 내용이 포함된다.

데이터의 Key를 claim이라고 하며 세 가지 종류로 나뉜다.

  • Registered claims (등록된 클레임)
  • Public claims (공개 클레임)
    • 사용자가 자유롭게 정의할 수 있는 claim이다.
    • 하지만 충돌을 피하기 위해 다음 두 가지 방식으로 정의해야 한다.
  • Private claims (비공개 클레임)
    • 당사자 간 정보 공유를 위한 맞춤형 claim
    • 사용을 동의한 당사자 간에 정보 공유를 위해 작성된다.
{
	"sub": "1234567890",
	"name": "John Doe",
	"admin": true
}

Signature

Signature는 인코딩된 Header, PayloadSecret Key가 합쳐진 형태로 생성된다.

예를 들어, HMAC SHA256 알고리즘을 사용하는 경우 다음과 같은 방식으로 생성된다.

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

세 부분이 각각 Base64Url로 인코딩되어 다음과 같은 형태로 보여진다.

jwt.io debugger를 사용해서 실제로 생성해볼 수 있다.

JWT의 문제

위에서 언급하였듯이 JWT는 만능이 아니다. 다른 인증 방식에 비하여 서버 부하가 적고, 보안에 강하기 때문에 많이 사용되고, 권장하는 방식이지만 무턱대고 사용하는 경우 큰 보안 이슈가 발생한다.

1. 알고리즘 지정

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

위와 같이 Header의 해시 알고리즘 부분을 “none”으로 해두는 경우이다.

어떤 멍청한 개발자가 이렇게 하냐 싶겠지만 생각보다 많은 사람들이 실수하는 부분이기 때문에 한번 더 확인하는 것을 추천드린다.

실제로 “none”을 집어넣고 해킹을 시도하는 경우도 존재하기 때문에 이를 방지하기 위해서 “none”인 경우를 걸러주는 로직도 구현할 필요가 있어보인다.

2. 유저 정보

JWT는 인코딩된 문자열이기는 하지만 디코딩이 매우 쉽다.

그렇기 때문에 JWT에는 절대로 민감한 유저 정보를 넣어서는 안되고, 해당 유저임을 확인할 수 있는 최소한의 정보만 넣어야 한다.

3. Secret Key 문제

Secret Key는 당연하게도 어렵게 만들어야 한다.

로그인에 필요한 비밀번호와 같은 것이기 때문에 유추가 쉬운 문자는 당연히 배제해야 하고, 최대한 길게 생성하고, 생성된 키는 철저하게 관리해야 한다.

이게 조금 불안하다 싶으면 많은 사람들이 사용하는 방법이 존재한다.

Private/Public Key Pair

생성과 검증에 필요한 공개 키와 비밀 키를 생성하는 것이다. 좀 더 쉽게 설명하자면 매우 풀기 어려운 수학적 문제(공개 키)와 출제자만 알고 있는 특정한 종류의 정보(비밀 키)를 생성한다. 문제(공개 키)를 풀면 원래의 메시지(비밀 키)를 해독할 수 있다.

이 방법을 사용하면 하나의 키를 사용할 때보다 키의 유출 위험이 줄어들고, 통신의 비밀성을 보장할 수 있다. 또한, 네트워크 규모가 커질수록 관리가 훨씬 용이해진다.

4. JWT 탈취

Session/Cookie 방식과 비슷하게 토큰을 탈취하는 경우가 발생할 수 있다. JWT는 클라이언트에서 관리되기 때문에 서버에서 이 문제를 해결할 수 있는 방법은 없다. 이를 방지하기 위해 유효기간이 존재하지만, 만료될 때까지 기다려야 한다는 문제가 있다. 그래서 이를 해결하기 위한 좋은 방법이 존재한다.

Access Token과 Refresh Token

기본적인 개념은 다음과 같다

  • Access Token
    • 유효기간이 짧은 토큰(ex. 60일, 1시간)
    • API 통신에 사용됨
  • Refresh Token
    • 유효기간이 긴 토큰(ex. 1년)
    • Access Token 갱신 시 사용됨

해당 방법을 사용한 통신 과정은 다음과 같다.

  1. 로그인 인증에 성공한 클라이언트는 Refresh Token과 Access Token 두 개를 서버로부터 받는다.
  2. 클라이언트는 Refresh Token과 Access Token을 로컬에 저장해놓는다.
  3. 클라이언트는 헤더에 Access Token을 넣고 API 통신을 한다. (Authorization)
  4. 일정 기간이 지나 Access Token의 유효기간이 만료되었다.
    1. Access Token은 이제 유효하지 않으므로 권한이 없는 사용자가 된다.
    2. 클라이언트로부터 유효기간이 지난 Access Token을 받은 서버는 401 (Unauthorized) 에러 코드로 응답한다.
    3. 401를 통해 클라이언트는 invalid_token (유효기간이 만료되었음)을 알 수 있다.
  5. 헤더에 Access Token 대신 Refresh Token을 넣어 API를 재요청한다.
  6. Refresh Token으로 사용자의 권한을 확인한 서버는 응답쿼리 헤더에 새로운 Access Token을 넣어 응답한다.
  7. 만약 Refresh Token도 만료되었다면 서버는 동일하게 401 error code를 보내고, 클라이언트는 재로그인해야한다.

Refresh Token 탈취?

해당 방법은 Access Token 탈취 문제를 해결하고자 만든 것이기 때문에 Refresh Token에 탈취 문제를 해결한 것은 아니다. OAuth는 이를 해결하기 위해 Refresh Token Rotation을 제시한다.

클라이언트가 Access Token을 재요청할 때 Refresh Token도 새로 발급받는 것이다. 이렇게 하면 탈취된 Refresh Token은 더 이상 해당 사용자의 Token이 아니게 되는 것이기 때문에 탈취 위험에서 벗어날 수 있게 된다.


Reference

JWT, Introduction, https://jwt.io/introduction
코딩애플, JWT 대충 쓰면 님들 인생 끝남, https://www.youtube.com/watch?v=XXseiON9CV0
Wikipedia, 공개 키 암호 방식, https://ko.wikipedia.org/wiki/공개암호_방식
OAuth, What Are Refresh Tokens and How to Use Them Securely, https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

profile
사회 공헌적인 Data Engineer를 꿈꾸는 이건입니다.

0개의 댓글