JWT란?

반응형
  • JWT는 다음 3개의 부분으로 구성된다.
xxxxx.yyyyy.zzzzz
  • 각 부분은 Base64로 인코딩되며, ‘.’ 으로 연결된 문자열이 최종 JWT이다.

부분 설명 예시

Header 토큰 타입과 서명 알고리즘 { "alg": "HS256", "typ": "JWT" }
Payload 토큰에 담을 데이터 (예: userId, email 등) { "sub": "123", "email": "abc@example.com" }
Signature 위 Header + Payload를 비밀 키로 서명한 값 HMACSHA256(base64(header) + "." + base64(payload), secret)

 

JWT는 어떻게 작동하나?

JWT 기반 인증 흐름

  1. 사용자가 로그인
    • ex) 소셜 로그인(Kakao, Google) 성공 시
  2. 서버가 JWT를 발급
    • 서버는 사용자 정보(ex: userId, email)를 담은 JWT를 서명(Signature)을 생성
    • JWT를 만들 때 비밀 키(Secret Key)를 사용해 서명
      • 비밀키는 밑에서 설명
  3. 프론트가 이 JWT를 저장
    • localStorage, sessionStorage 또는 Cookie에 저장
  4. 이후 요청에 JWT 포함
    • 매 요청 시 HTTP 헤더에 JWT를 포함
    Authorization: Bearer <JWT>
    
  5. 서버는 이 JWT를 검증하고 사용자 인증 완료
    • JWT 안에 이미 userId가 들어있기 때문에 DB 조회 없이 인증 가능

비밀 키(서명 signature)의 역할

  • 이 서명 덕분에 나중에 누군가 JWT를 위조했는지를 판별할 수 있다.

즉, 서버가 발급한 진짜 JWT인지 아닌지를 구별하는 핵심이 바로 이 비밀 키이다.

JWT는 다음과 같은 방식으로 만들어진다

JWT = Base64UrlEncode(Header) + "." + Base64UrlEncode(Payload) + "." + Signature

서명(Signature)는 다음과 같이 생성된다.

  • 서버는 이 과정을 거쳐 토큰을 발급한다.
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secretKey
)

 

 

서버는 비밀 키로 서명, 검증도 서버만 가능

단계 설명

JWT 발급 서버가 비밀 키를 이용해서 토큰 서명(Signature) 생성
JWT 전달 프론트에 JWT 전달
JWT 인증 요청 프론트가 JWT를 Authorization 헤더로 서버에 보냄
JWT 검증 서버가 비밀 키로 Signature가 위조되지 않았는지 확인

Spring boot에서는 보통 다음과 같이 관리를 한다.

# application.yml
jwt:
  secret: MyVerySecretKeyThatIsLongEnough
  expiration: 86400000  # 24시간
@Value("${jwt.secret}")
private String secretKey;

JWT는 서버가 비밀 키로 서명해서 만든 토큰이고, 그 비밀 키 덕분에 위조 방지와 인증 검증이 가능하다.

 

 

주의할 점

  • Payload는 암호화되지 않음! (단순 인코딩이라서 열어볼 수 있음)
  • 중요한 정보(비밀번호 등)는 절대 Payload에 넣지 말 것
  • 비밀 키가 노출되면 서명을 위조할 수 있으므로 서버에서만 안전하게 관리

 

Access Token과 Refresh Token 개념 추가

Access Token과 Refresh Token이란?

  • 곧 서버에서 발급해주는 JWT가 결국 Access Token이라고 보면 된다.
  • Refresh Token은 그 AccessToken의 유효성을 "지속적으로 보장하고 보안성을 강화"하는 역할을 한다

구분 설명

Access Token JWT 형식으로 발급된 인증 토큰. 클라이언트가 API 요청 시마다 서버에 인증용으로 사용
Refresh Token Access Token이 만료되었을 때 새로운 Access Token을 발급받기 위한 장기 토큰. 보통도 JWT이지만, UUID 형태일 수도 있음

 

JWT인 accesstoken은 유효기간이 있다

왜?

Access Token(JWT)은 노출(탈취)될 수 있는 위험이 있기 때문에, 피해를 최소화하기 위해 유효기간이 꼭 필요하다.

그 이유는

  • 보통 localStorage, sessionStorage, 심지어 모바일 앱 내부 DB에 저장됨
    • XSS, CSRF, 디컴파일링 등으로 탈취될 수 있는 위치에 있음
    • 이 상태에서 유효기간이 없으면? → 토큰을 훔쳐간 해커가 평생 인증 가능
  • Authorization 헤더로 전송 → 중간자 공격(MITM)에서 노출될 수 있음 (HTTPS 미사용 시)
    • 탈취되어도 토큰 유효기간이 짧다면 해커는 오래 못 씀 → 피해 축소
  • 서버는 Access Token을 기억하지 않음 → 탈취 감지 불가
    • 따라서 주기적 만료만이 안전장치 역할

 

시나리오는 다음과 같다.

시나리오 Access Token 만료가 없으면 Access Token 만료가 있으면

해커가 localStorage에서 탈취 평생 사용자로 위장 가능 짧은 시간 뒤 만료 → 피해 최소화
중간자 공격으로 토큰 유출 모든 API 접근 가능 일정 시간 후 자동 차단됨
브라우저가 감염됨 자동 로그인 유지됨 주기적으로 재인증 필요

그러면 Access token의 유효기간은 짧다. 그러면 사용자가 계속 로그인을 자주해야되는 불편함이 생겨버림

이걸 해결하고자 Refresh Token 사용

Refresh Token 도입

  • Access Token은 짧게: 15분
  • Refresh Token은 길게: 7일~30일
  • Access Token이 만료되면 → Refresh Token으로 재발급 요청

JWT는 어떤 해킹에서로부터 막아줄까?

해킹 시나리오 3가지를 기준으로 한 번 보자

 

1. 세션 탈취(Session Hijacking)

세션 방식 인증 구조부터 한 번 보면

세션 방식 인증 구조

 

여기서 세션 탈취가 어떻게 일어날까?

session 탈취 시나리오

  • 서버는 쿠키만 보고 판단하기 때문에 누가 보냈는지 식별이 불가
  • 그래서 세션 기반 인증은 쿠키 탈취에 매우 취약

 

JWT를 사용했을 때 세션 탈취가 무의미한 이유는?

  • 세션 방식은 서버가 사용자 상태를 기억해야한다
    • 서버가 sessionId로 유저 상태를 “기억”하고 있어야함
    • sessionId만 탈취되면 그 유저가 되어버림
  • JWT는 서버가 사용자 상태를 기억하지 않아도 된다.
    • 모든 인증 정보가 JWT 안에 들어 있고 서버는 검증만 하면 됨
    • 서버 입장에서는 이 JWT가 유효한가?만 판단하면 됨

즉 서버는 상태를 기억하지 않기 때문에 세션을 탈취했다는 개념 자체가 존재하지 않음

 

 

2. 토큰 위조(Token Forgery)

토큰 위조 시나리오

JWT기반이면

  • JWT에는 서버의 비밀 키로 서명된 Signature가 포함되어 있어서 누구도 위조 불가, 서명이 다르면 검증 실패
  • 서버는 항상 서명을 검증하므로 해커가 사용자 ID를 바꿔도 무효

 

 

3. Replay Attack(재사용 공격)

재사용 공격 흐름

 

  • 해커가 refresh token을 탈취해서 서버에게 Accesstoken 발급 요청을 해서 해커가 그 Accesstoken으로 사용자처럼 행동할 수 있는 것임

 

이걸 해결하기 위해 서버는 다음과 같이 행동할 수 있다.

 

1. Refresh Token을 서버에 저장 (DB 또는 Redis)

  • 토큰을 발급할 때 서버에 같이 저장
  • 재요청 시 DB의 토큰과 일치하는지 비교

2. Refresh Token Rotation (회전 방식)

  • 한 번 사용된 Refresh Token은 즉시 폐기
  • 새 Refresh Token 발급하고 이전 건 무효화
  • 이전 토큰이 다시 쓰이면 공격으로 간주 → 사용자 로그아웃, 강제 만료

3. 사용자, IP, UA(Device) 추적

  • Refresh 요청이 다른 디바이스/IP/User-Agent에서 오면 차단 or 인증 요구
반응형