본문 바로가기

Backend/Study

[Study] JWT 토큰 인증 방식과 보안 고려사항

반응형

JWT 토큰 인증 방식과 보안 고려사항

오늘은 백엔드 개발에서 가장 많이 사용되는 인증 방식 중 하나인 JWT(JSON Web Token) 토큰 인증에 대해 정리해보겠습니다.
JWT의 정의 → 사용 방식 → Access/Refresh 토큰 구조 → 관리 방법 → 보안 고려사항까지 단계별로 살펴보겠습니다.


JWT(JSON Web Token) 토큰이란?

  • JSON 형식으로 인코딩된 사용자 인증 정보를 포함하는 토큰입니다.
  • 주로 클라이언트-서버 간 인증을 위해 사용되며, 세션을 서버에 저장하지 않고도 인증 상태를 유지할 수 있습니다.

JWT는 크게 세 부분으로 구성됩니다:

Header.Payload.Signature

예시:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VySWQiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  • Header: 토큰 타입(JWT), 알고리즘(예: HS256)
  • Payload: 사용자 정보(Claims: userId, role 등)
  • Signature: 비밀 키로 서명, 변조 방지

JWT 인증 방식의 흐름

  1. 로그인 요청: 사용자가 아이디/비밀번호로 로그인
  2. 토큰 발급: 서버가 Access Token(+Refresh Token)을 발급하여 클라이언트에 전달
  3. 인증 요청: 클라이언트가 API 호출 시 Authorization: Bearer <Access Token> 헤더에 포함
  4. 서버 검증: 서버는 토큰을 검증하여 권한 확인 후 응답
  5. 토큰 갱신: Access Token 만료 시, Refresh Token으로 새로운 Access Token 발급

이 과정에서 서버는 세션을 저장하지 않아 확장성(Scalability)이 뛰어납니다.


Access Token과 Refresh Token

구분 Access Token Refresh Token
목적 API 요청 시 인증/인가 수행 Access Token 만료 시 갱신
유효기간 짧음 (분~시간 단위) 김 (일~주 단위)
저장 위치 보통 브라우저 메모리/앱 내부 HttpOnly 쿠키 또는 안전한 DB
보안 리스크 탈취 시 즉시 API 접근 가능 탈취 시 장기 인증 위험
관리 방법 빠른 만료, 정기 재발급 안전한 저장 + 재발급 시 무효화 정책

즉, Access Token은 짧게, Refresh Token은 길게 가져가면서 보안을 강화하는 구조가 일반적입니다.


JWT 관리 전략

  1. 토큰 저장 위치
    • Access Token → 메모리, 혹은 localStorage (단, XSS 취약점 고려)
    • Refresh Token → HttpOnly Secure 쿠키 권장
  2. 토큰 만료 전략
    • Access Token: 수 분 ~ 수 시간 단위
    • Refresh Token: 수 일 ~ 수 주 단위
    • 토큰 탈취 위험을 최소화하려면 짧게 설정하는 것이 핵심
  3. 토큰 무효화(Invalidate)
    • 사용자가 로그아웃할 때 Refresh Token DB에서 삭제
    • Refresh Token 블랙리스트 관리(중앙 집중 저장소 or Redis 활용)
  4. 재발급 로직
    • Access Token 만료 시 Refresh Token을 사용해 새 Access Token 발급
    • Refresh Token도 만료되면 재로그인 요구

JWT 보안 고려사항

1. 토큰 탈취 방지

  • HTTPS 사용: 중간자 공격 방지
  • HttpOnly 쿠키: JS 접근 차단 → XSS 완화
  • Secure 옵션: HTTPS 연결에서만 전송

2. 짧은 만료 시간 설정

  • Access Token은 최대 수 시간, Refresh Token은 며칠~몇 주 단위
  • 너무 길면 탈취 시 위험이 커짐

3. 토큰 무효화 정책

  • 강제 로그아웃/권한 변경 시 즉시 블랙리스트 처리 필요
  • Redis 같은 중앙 저장소 활용

4. Payload에 민감정보 저장 금지

  • JWT는 클라이언트에서도 디코딩 가능(Base64 URL Safe 인코딩)
  • 비밀번호, 카드번호 같은 민감정보는 절대 넣지 말 것

5. 서명 키 관리

  • 대칭키(HS256): 키가 노출되면 심각한 위험
  • 비대칭키(RS256): 공개키/개인키 분리로 안전성↑

6. Refresh Token 보안

  • DB에 안전하게 저장하고, 기기별 Refresh Token을 별도로 관리
  • 재발급 시 기존 토큰 폐기

예제 코드 (Spring Boot + JWT)

1) JWT 발급 유틸

public class JwtUtil {
    private static final String SECRET = "mySecretKey";
    private static final long EXPIRATION = 1000 * 60 * 15; // 15분

    public static String generateToken(String userId) {
        return Jwts.builder()
                .setSubject(userId)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
                .signWith(SignatureAlgorithm.HS256, SECRET)
                .compact();
    }

    public static Claims validateToken(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .getBody();
    }
}

2) 컨트롤러 예제

@RestController
public class AuthController {

    @PostMapping("/login")
    public Map<String, String> login(@RequestBody Map<String, String> body) {
        String userId = body.get("userId");
        // 비밀번호 검증 로직 생략
        String token = JwtUtil.generateToken(userId);
        return Map.of("accessToken", token);
    }

    @GetMapping("/secure")
    public String secure(@RequestHeader("Authorization") String authHeader) {
        String token = authHeader.replace("Bearer ", "");
        Claims claims = JwtUtil.validateToken(token);
        return "Hello " + claims.getSubject();
    }
}

정리

  • JWT 토큰은 세션 저장소가 필요 없는 인증 방식으로 확장성과 편의성을 제공.
  • Access Token은 짧게, Refresh Token은 길게 가져가면서 보안을 강화.
  • 보안 고려사항: HTTPS, HttpOnly 쿠키, 짧은 만료시간, 블랙리스트, 민감정보 금지, 키 관리.
  • 대규모 서비스에서는 반드시 Refresh Token 관리 정책토큰 무효화 전략을 함께 설계해야 함.
반응형