[CS] 쿠키, 세션, 토큰

4rk·2025년 1월 16일
post-thumbnail

목차

인증 방식의 이해

쿠키의 역할: 클라이언트 측 저장소

쿠키는 단순히 브라우저의 데이터 저장소 역할을 합니다. 다음과 같은 데이터를 저장할 수 있습니다:

  1. 세션 ID를 저장하는 경우
Cookie: SESSIONID=abc123...
  1. JWT 토큰을 저장하는 경우
Cookie: jwt=eyJhbGciOiJIUzI1NiIs...
  1. 둘 다 저장하는 경우
Cookie: SESSIONID=abc123...
Cookie: jwt=eyJhbGciOiJIUzI1NiIs...

인증 데이터의 형태

  1. 세션 ID
  • 서버의 세션 저장소와 매핑되는 식별자
  • 실제 데이터는 서버에 저장
  • 쿠키를 통해 세션 ID 전달
  1. 토큰 (JWT 등)
  • 자체적으로 정보를 포함
  • 서버 저장소가 필요 없음
  • 쿠키 또는 Authorization 헤더로 전달

저장 방식의 선택

인증 방식의 선택은 "쿠키냐 세션이냐 토큰이냐"의 문제가 아니라:

  1. 인증 정보를 어떤 형태로 관리할 것인가?
    • 서버 세션 방식
    • 토큰 방식
  2. 클라이언트에서 어떻게 저장할 것인가?
    • 쿠키에 저장
    • 로컬 스토리지에 저장
    • 메모리에 저장

쿠키 기반 인증

구현 패턴

특징

  • 서버 측에서 Set-Cookie 헤더를 통해 클라이언트에 쿠키 전달
  • 브라우저가 자동으로 쿠키를 저장하고 요청시 전송
  • HttpOnly, Secure, SameSite 등의 보안 옵션 설정 가능

장점

  • 구현이 간단
  • 자동으로 쿠키 전송
  • 메모리 사용량이 적음

단점

  • XSS, CSRF 공격에 취약할 수 있음
  • 도메인에 종속적
  • 쿠키 크기 제한 (대체로 4KB)

세션 기반 인증

구현 패턴

특징

  • 서버 측에 세션 정보 저장
  • 클라이언트는 SessionID만 보유
  • Redis 등의 세션 스토어 사용 가능

장점

  • 서버 측에서 세션 제어 가능
  • 상대적으로 높은 보안성
  • 클라이언트에 민감 정보 노출 최소화

단점

  • 서버 메모리 사용량 증가
  • 수평 확장시 세션 공유 필요
  • 서버 의존성이 높음

JWT 토큰 기반 인증

구현 패턴

특징

  • 토큰에 필요한 정보를 모두 포함 (self-contained)
  • Stateless 아키텍처 지원
  • Header.Payload.Signature 구조

장점

  • 서버 부하 감소
  • 확장성이 좋음
  • Cross-Origin 요청 처리 용이
  • Microservice 아키텍처에 적합

단점

  • 토큰 크기가 상대적으로 큼
  • 토큰 만료 전 철회가 어려움
  • 토큰 탈취시 보안 위험

보안 고려사항

쿠키/세션 보안

  • HTTPS 사용 필수
  • Secure 플래그 설정
  • HttpOnly 플래그 설정
  • SameSite 속성 설정
  • CSRF 토큰 사용

JWT 보안

  • 민감한 정보는 Payload에 포함하지 않기
  • 짧은 만료 시간 설정
  • Refresh Token 순환
  • 서명 알고리즘 보안 강화 (HS256 이상)
  • 토큰 저장소 보안

구현 예제

Spring Security 설정 예시

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

JWT 구현 예시

public String generateToken(UserDetails userDetails) {
    Map<String, Object> claims = new HashMap<>();
    claims.put("sub", userDetails.getUsername());
    claims.put("created", new Date());

    return Jwts.builder()
        .setClaims(claims)
        .setExpiration(new Date(System.currentTimeMillis() + TOKEN_VALIDITY))
        .signWith(SignatureAlgorithm.HS512, secret)
        .compact();
}

각 인증 방식의 적용 영역

쿠키의 적용 영역

  • 웹 브라우저 환경
    • 사용자 선호도 저장 (다크모드, 언어 설정 등)
    • 장바구니 정보 유지
    • "로그인 유지하기" 기능
  • 웹 분석 및 추적
    • 사용자 행동 분석
    • 광고 트래킹
  • 세션 관리 보조
    • 세션 ID 저장
    • 인증 토큰 저장

세션의 적용 영역

  • 웹 애플리케이션
    • 사용자 인증 상태 관리
    • 장바구니 등 임시 데이터 관리
  • 모바일 앱
    • 앱 사용자 세션 관리
    • 임시 상태 저장
  • 게임 서버
    • 플레이어 세션 관리
    • 게임 상태 유지
  • 실시간 애플리케이션
    • 채팅 세션
    • 화상 통화 세션
  • 데이터베이스
    • 연결 세션 관리
    • 트랜잭션 관리

토큰의 적용 영역

  • API 인증
    • RESTful API 접근 제어
    • 마이크로서비스 간 인증
  • 모바일 앱
    • 사용자 인증
    • API 접근 권한 관리
  • 크로스 플랫폼 서비스
    • Single Sign-On (SSO)
    • OAuth 기반 인증
  • IoT 기기
    • 기기 인증
    • 서비스 접근 제어
  • 클라우드 서비스
    • 서비스 간 인증
    • 리소스 접근 제어
  • 서드파티 통합
    • API 키 관리
    • 권한 위임

인증 방식 상세 비교

세션 기반 인증

장점

  • 서버 측에서 완벽한 제어 가능
  • 세션 데이터 즉시 무효화 가능
  • 상대적으로 작은 쿠키 크기 (세션 ID만 저장)
  • 클라이언트에 중요 정보 노출 없음

단점

  • 서버 메모리 사용
  • 확장 시 세션 클러스터링 필요
  • 여러 도메인에서 사용 어려움
  • CORS 이슈 발생 가능

적합한 상황

  • 단일 도메인 애플리케이션
  • 빠른 세션 무효화가 필요한 경우
  • 보안이 중요한 금융 서비스
  • 실시간 사용자 상태 관리가 필요한 경우

토큰 기반 인증 (JWT)

장점

  • 서버 무상태 유지 가능
  • 수평적 확장 용이
  • 다중 도메인 지원
  • 모바일 앱 연동 용이

단점

  • 토큰 크기가 상대적으로 큼
  • 만료 전 토큰 무효화 어려움
  • 토큰 탈취 시 보안 위험
  • 많은 데이터를 포함할수록 네트워크 부하 증가

적합한 상황

  • 마이크로서비스 아키텍처
  • 모바일 앱과 웹 통합 서비스
  • 서버 확장이 잦은 환경
  • 다중 도메인 인증이 필요한 경우

보안 고려사항

세션 보안

  1. 세션 고정 공격 대응

    // Spring Security 설정 예시
    http.sessionManagement()
        .sessionFixation()
        .newSession(); // 로그인 시 새로운 세션 생성
  2. 세션 타임아웃

    http.sessionManagement()
        .maximumSessions(1) // 동시 세션 제한
        .expiredUrl("/login?expired")
        .and()
        .sessionFixation().migrateSession()
        .invalidSessionUrl("/login?invalid");
  3. 세션 ID 보호

    • 암호화된 세션 ID 사용
    • HttpOnly 플래그 설정
    • Secure 플래그 설정

JWT 보안

  1. 토큰 설계

    public String generateToken(UserDetails userDetails) {
        return Jwts.builder()
            .setSubject(userDetails.getUsername())
            .setIssuedAt(new Date(System.currentTimeMillis()))
            .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY))
            .signWith(SignatureAlgorithm.HS512, secret)
            .compact();
    }
  2. 토큰 저장

    • HttpOnly 쿠키 사용 권장
    • 로컬 스토리지 사용 시 XSS 위험 고려
    // 취약한 방식
    localStorage.setItem('token', jwt);
    
    // 권장 방식
    document.cookie = `jwt=${token}; HttpOnly; Secure; SameSite=Strict`;
  3. 토큰 갱신 전략

    @PostMapping("/refresh")
    public ResponseEntity<?> refreshToken(HttpServletRequest request) {
        String refreshToken = extractRefreshToken(request);
        if (jwtUtil.validateRefreshToken(refreshToken)) {
            String newAccessToken = jwtUtil.generateNewAccessToken(refreshToken);
            return ResponseEntity.ok(new TokenResponse(newAccessToken));
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }

성능과 확장성

세션 스케일링

  1. Sticky Session

    # nginx 설정 예시
    upstream backend {
        ip_hash; # 같은 사용자는 같은 서버로
        server backend1.example.com;
        server backend2.example.com;
    }
  2. 세션 클러스터링

    // Spring Session with Redis
    @EnableRedisHttpSession
    public class SessionConfig {
        @Bean
        public LettuceConnectionFactory connectionFactory() {
            return new LettuceConnectionFactory();
        }
    }

JWT 성능 최적화

  1. 페이로드 최소화

    // Bad: 너무 많은 정보 포함
    claims.put("user", userDetails);
    
    // Good: 필요한 정보만 포함
    claims.put("sub", userDetails.getUsername());
    claims.put("roles", userDetails.getAuthorities().stream()
        .map(GrantedAuthority::getAuthority)
        .collect(Collectors.toList()));
  2. 캐싱 전략

    // 자주 사용되는 토큰 검증 결과 캐싱
    @Cacheable(value = "tokenCache", key = "#token")
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }

구현 시 고려사항

저장소 선택

  1. 쿠키 사용 시

    • 4KB 크기 제한
    • HttpOnly 설정 가능
    • 자동 전송 장점
    • XSS/CSRF 대비 필요
  2. 로컬 스토리지 사용 시

    • 더 큰 용량 저장 가능
    • JavaScript로 접근 관리
    • XSS에 취약할 수 있음
    • 수동으로 헤더에 포함 필요

인증 데이터 형식 선택

  1. 세션 방식 선택 시

    • 서버 저장소 필요
    • 세션 클러스터링 고려
    • 빠른 세션 무효화 가능
    • 메모리 사용량 고려
  2. 토큰 방식 선택 시

    • 서버 무상태 가능
    • 토큰 크기 고려
    • 만료 전 폐기 방안 필요
    • 토큰 갱신 전략 필요

현대적 인증 시스템의 특징

  • 다중 요소 인증 (MFA) 지원
  • 세밀한 접근 제어 (Fine-grained Access Control)
  • 실시간 토큰 검증
  • 높은 확장성
  • 플랫폼 독립적
  • 보안 표준 준수

참고 자료

profile
4rk의 프로그래밍 스터디

0개의 댓글