프론트엔드 개발

JWT 기술분석: Access Token과 Refresh Token 전략

쫌수 2025. 1. 17. 17:50

목차

  1. JWT(JSON Web Token) 개요
  2. Access Token vs Refresh Token
  3. 토큰 관리 전략
  4. 보안 고려사항
  5. 구현 모범 사례

1. JWT(JSON Web Token) 개요

1.1 JWT란?

JWT(JSON Web Token)는 당사자 간에 정보를 JSON 객체로 안전하게 전송하기 위한 독립적인 방식을 정의하는 개방형 표준(RFC 7519)입니다. 이 정보는 디지털 서명되어 있으므로 신뢰할 수 있습니다.

1.2 JWT의 구조

JWT는 다음과 같은 세 부분으로 구성됩니다:

  • Header: 토큰 유형과 사용된 서명 알고리즘
  • Payload: 클레임(claim)들을 포함하는 실제 데이터
  • Signature: 토큰의 유효성을 검증하기 위한 서명

예시:

header.payload.signature

2. Access Token vs Refresh Token

2.1 Access Token

  • 정의: 보호된 리소스에 접근하기 위한 credentials을 담고 있는 토큰
  • 특징:
    • 비교적 짧은 유효기간 (보통 15분~2시간)
    • 모든 API 요청에 포함되어 전송
    • 탈취 위험을 줄이기 위해 짧은 수명 유지

2.2 Refresh Token

  • 정의: 새로운 access token을 발급받기 위한 토큰
  • 특징:
    • 긴 유효기간 (보통 2주~수개월)
    • 서버 측에서 안전하게 저장 관리
    • Access token이 만료되었을 때만 사용

3. 토큰 관리 전략

3.1 토큰 저장 전략

클라이언트 측:

// Access Token: 메모리에 저장
let accessToken = response.accessToken;

// Refresh Token: HttpOnly 쿠키에 저장
document.cookie = `refreshToken=${response.refreshToken}; HttpOnly; Secure; SameSite=Strict`;

서버 측:

// Refresh Token 저장 예시 (Redis 사용)
await redis.set(
  `refresh_token:${userId}`,
  refreshToken,
  'EX',
  60 * 60 * 24 * 14 // 14일 유효기간
);

3.2 토큰 갱신 전략

  1. Silent Refresh

    • Access Token 만료 직전에 자동으로 갱신
    • 백그라운드에서 사용자 경험 방해 없이 처리
  2. Rotation 전략

    • Refresh Token 사용 시마다 새로운 Refresh Token 발급
    • 토큰 탈취 시 피해 최소화
// Refresh Token Rotation 구현 예시
async function rotateTokens(oldRefreshToken) {
  const { userId } = verifyRefreshToken(oldRefreshToken);

  // 기존 Refresh Token 무효화
  await invalidateRefreshToken(oldRefreshToken);

  // 새로운 토큰 쌍 생성
  const accessToken = generateAccessToken(userId);
  const refreshToken = generateRefreshToken(userId);

  // 새 Refresh Token 저장
  await storeRefreshToken(userId, refreshToken);

  return { accessToken, refreshToken };
}

4. 보안 고려사항

4.1 토큰 보안

  • Access Token은 메모리에만 저장
  • Refresh Token은 HttpOnly 쿠키로 관리
  • 모든 통신은 HTTPS 사용
  • 적절한 만료 시간 설정

4.2 취약점 대응

  1. XSS (Cross-Site Scripting)

    • HttpOnly 쿠키 사용
    • Content Security Policy (CSP) 적용
  2. CSRF (Cross-Site Request Forgery)

    • SameSite 쿠키 속성 사용
    • CSRF 토큰 추가 구현
  3. Token Theft

    • Refresh Token Rotation
    • 토큰 무효화 메커니즘 구현

5. 구현 모범 사례

5.1 토큰 설계

// Access Token Payload
{
  "sub": "1234567890",     // Subject (User ID)
  "name": "John Doe",      // 사용자 이름
  "roles": ["user"],       // 권한
  "iat": 1516239022,      // 발급 시각
  "exp": 1516239922       // 만료 시각
}

// Refresh Token Payload
{
  "sub": "1234567890",     // Subject (User ID)
  "iat": 1516239022,
  "exp": 1517239022,
  "jti": "unique-token-id" // 토큰 고유 식별자
}

5.2 에러 처리

async function handleTokenError(error) {
  if (error.name === 'TokenExpiredError') {
    try {
      const newTokens = await refreshTokens();
      return retryRequestWithNewToken(newTokens.accessToken);
    } catch (refreshError) {
      // Refresh Token도 만료된 경우
      logout();
      redirectToLogin();
    }
  }

  if (error.name === 'JsonWebTokenError') {
    // 유효하지 않은 토큰
    logout();
    redirectToLogin();
  }
}

5.3 보안 설정 예시

// JWT 옵션 설정
const jwtOptions = {
  algorithm: 'RS256',              // 비대칭 암호화 사용
  expiresIn: '1h',                // Access Token 만료시간
  audience: 'https://api.example.com',
  issuer: 'https://auth.example.com'
};

// 쿠키 설정
const cookieOptions = {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  path: '/refresh',
  maxAge: 14 * 24 * 60 * 60 * 1000 // 14일
};

결론

JWT 기반의 인증 시스템을 구현할 때는 다음 사항을 고려해야 합니다:

  1. Access Token과 Refresh Token의 역할 분리
  2. 적절한 토큰 저장 방식 선택
  3. 보안 위협에 대한 대비
  4. 효율적인 토큰 갱신 메커니즘 구현
  5. 체계적인 에러 처리

이러한 요소들을 적절히 조합하여 구현하면, 안전하고 사용자 친화적인 인증 시스템을 구축할 수 있습니다.