[SB 3기] 코드잇 스프린트 위클리페이퍼 14주차

JHLee·2025년 8월 10일
post-thumbnail

Q. Spring 기반 웹 애플리케이션에서 발생할 수 있는 4가지 주요 보안 공격 (CSRF, XSS, 세션 고정, JWT 탈취)에 대해 설명하고, 각각에 대한 Spring Security 또는 일반적인 대응 전략을 설명하세요.


👾 1. CSRF (Cross-Site Request Forgery)

  • 사용자의 인증 상태(세션/쿠키)를 악용해 사용자가 의도하지 않은 요청을 전송하도록 만드는 공격

공격 원리:

  1. 사용자가 서비스에 로그인(쿠키 발급)
  2. 공격 페이지 유도
  3. 사용자 브라우저가 쿠키를 자동 포함해 요청 전송
  4. 서버는 사용자의 정상 요청으로 오인
  5. 의도치 않은 동작 수행

대응 전략:

  • CSRF 토큰 사용: 서버가 토큰 발급 -> 클라이언트가 요청 헤더로 회송
  • 보호 범위: 상태 변경 메서드(POST/PUT/PATCH/DELETE)에서 토큰 강제
  • 쿠키 옵션: SameSite=Lax|Strict, Secure, HttpOnly
    ➡️ ⭐️ 서버가 준 CSRF 토큰을 요청에 실어 보내고, 쿠키는 SameSite로 잠그기

Spring Security 설정 예시:

http
  .csrf(csrf -> csrf
    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
  );

⚠️ 기본 헤더명은 "X-XSRF-TOKEN"이므로, "X-CSRF-TOKEN"을 사용하려면CookieCsrfTokenRepository에서 직접 지정해주어야 한다. (repo.setHeaderName("X-CSRF-TOKEN");)


💉 2. XSS (Cross-Site Scripting)

  • 입력값에 악성 스크립트를 삽입하여 사용자의 브라우저에서 실행시키는 공격
  • 종류 : Stored, Reflected, DOM

공격 원리:

  1. 공격자가 스크립트가 포함된 데이터를 서버/클라이언트 경로로 주입
  2. 브라우저가 이를 코드로 해석하고 실행

Stored / Reflected / DOM XSS

종류공격 방식
Stored XSS (저장형)악성 스크립트를 서버에 저장 후, 사용자가 해당 데이터를 불러올 때 실행
Reflected XSS (반사형)악성 스크립트를 요청 파라미터나 URL에 담아 서버 응답에 즉시 반영
DOM XSS서버 응답을 거치지 않고, 클라이언트 측(JavaScript)에서 DOM 조작 시 발생

대응 전략:

  • 출력 인코딩: 템플릿 엔진 기본 이스케이프(th:text) 사용
  • Sanitizer 사용: 리치 텍스트는 화이트리스트 기반 HTML Sanitizer 적용
  • DOM 조작 주의: innerHTML 등 신뢰되지 않은 HTML 직접 삽입 금지
  • 보안 헤더 설정: CSP(Content Security Policy), X-Content-Type-Options, X-Frame-Options 등 적용

Spring Security 설정 예시:

http.headers(h -> h
  // CSP 설정
  .contentSecurityPolicy(csp -> csp.policyDirectives(
    "default-src 'self'; " +
    "script-src 'self'; " +
    "object-src 'none'; " +
    "base-uri 'self'; " +
    "frame-ancestors 'none'"))
  .xssProtection(x -> x.disable())		// 구형 XSS 필터 비활성 → CSP로 대체
  .contentTypeOptions(withDefaults())	// MIME 타입 스니핑 방지
  .frameOptions(frame -> frame.deny())	// 클릭재킹 방지
  // HTTPS 강제
  .httpStrictTransportSecurity(hsts -> hsts	 
    .maxAgeInSeconds(31536000) // 1년
    .includeSubdomains(true)
  )
);

🔓 3. 세션 고정 공격 (Session Fixation)

  • 공격자가 미리 정한 세션 ID를 사용자가 쓰도록 유도하고, 인증 후에도 같은 세션을 공유해 권한을 탈취하는 공격

공격 원리:

  1. 로그인 전 세션을 공격자가 고정
  2. 로그인 후에도 동일 세션 ID 유지 시 탈취

대응 전략:

  • 로그인 시 세션 재발급: migrateSession(기본값)
  • 로그아웃 시 세션 무효화
  • URL Rewriting 금지: JSESSIONID가 URL에 노출되지 않도록 설정
  • 쿠키 옵션: HttpOnly, Secure, SameSite
  • 동시 세션 제한 설정

Spring Security 설정 예시:

http.sessionManagement(sm -> sm
  .sessionFixation(sf -> sf.migrateSession())  // 로그인 시 세션 재발급
  .maximumSessions(1)                          // 동시 세션 제한
  .maxSessionsPreventsLogin(false)
);

application.yml 예시:

server:
  servlet:
    session:
      tracking-modes: cookie   # URL에 JSESSIONID 붙는 것 차단
      cookie:
        same-site: Lax
        secure: true

🪪 4. JWT 탈취

  • Access/Refresh 토큰이 유출되면 만료 전까지 임의 호출 가능

공격 원리:

  • XSS, 피싱, 로컬스토리지 노출, 로그 유출, 네트워크 스니핑(비TLS) 등으로 토큰 획득

대응 전략:

  • 만료 전략: 짧은 Access Token + 긴 Refresh Token으로 설계
  • 토큰 회전(Token Rotation) + 재사용 감지(jti 기반)
  • 저장 위치 전략
    • 쿠키: HttpOnly , Secure, SameSite + CSRF 방어
    • 메모리/헤더(Bearer): CSRF엔 강하지만 XSS 방어 철저
  • 서명 검증: iss, aud, exp 등 검증
  • 네트워크 보안: HTTPS 강제, CORS 최소 허용, 토큰 로깅 금지

Spring Security 설정 예시:

http
  .oauth2ResourceServer(o -> o
    .jwt(jwt -> jwt.jwtAuthenticationConverter(customConverter))
  )
  .authorizeHttpRequests(auth -> auth
    .requestMatchers("/api/public/**").permitAll()
    .anyRequest().authenticated()
  );

// jti 기반 재사용 차단
boolean isRevoked(String jti){ return redis.hasKey("revoked:"+jti); }
void revoke(String jti, Duration ttl){ redis.set("revoked:"+jti, "1", ttl); }

Q. JWT(JSON Web Token)의 구조와 각 구성 요소가 어떤 역할을 하는지 구체적으로 설명하세요.


✅ JWT란?

  • JWT는 JSON 객체를 안전하게 전송하기 위한 토큰 표준(RFC 7519)이다.
  • 토큰 자체가 필요한 인증/인가 정보를 모두 포함하고 있어, 서버는 별도의 세션 저장소 없이도 사용자 상태를 확인할 수 있다.
  • 주로 API 인증·인가, SSO(Single Sign-On) 등에서 사용된다.

⚠️ JWT는 기본적으로 서명(Signature)으로 위·변조 여부만 검증하며, 암호화는 하지 않는다.
누구나 Payload를 디코딩할 수 있으므로, 민감 정보는 절대 포함하면 안된다.


🔍 JWT의 구조

  • JWT는 .(점)으로 구분된 세 부분(Header,Payload,Signature)으로 구성된다.
  • 각 부분은 Base64URL 인코딩되어 있으며, 디코딩하면 원본 JSON을 확인할 수 있다.
  xxxxx.yyyyy.zzzzz
   ↑      ↑     ↑
 Header Payload Signature

1. Header

  • 토큰의 타입(typ)과 서명 알고리즘(alg), 필요 시 키 식별자(kid)를 포함한다.

  • 예시

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-2025-08"
}
  • alg: 서명 알고리즘 (예: HS256, RS256, ES256) -> none 사용 금지
  • typ: 토큰 타입 (일반적으로 "JWT")
  • kid: Key ID — 키 로테이션 시 어떤 키를 사용할지 식별

2. Payload

  • 토큰에 담길 클레임(Claim) 정보로 인증/인가에 필요한 식별자, 권한, 메타데이터 등을 포함한다.

  • 예시

{
  "sub": "user-123",
  "name": "Gil Dong",
  "exp": 1754803200,
  "iat": 1754796000,
  "scope": "read write",
  "roles": ["USER", "ADMIN"],
  "email": "user@example.com"
}

클레임 종류

1) 표준 클레임 (Registered Claims) : 사전에 정의된 예약 키

  • iss (issuer): 토큰 발급자
  • sub (subject): 토큰 주체 (사용자 ID)
  • aud (audience): 토큰 대상
  • exp (expiration): 만료 시각
  • iat (issued at): 발급 시각
  • nbf (not before): 토큰 활성화 시작 시각
  • jti (jwt id) : 토큰 고유 식별자 (재사용 감지에 활용)

2) 공개 클레임 (Public Claims) : 표준 외의 일반 키, 충돌 방지 위해 네임스페이스(주로 URL 형태) 권장

3) 비공개 클레임 (Private Claims) : 발급자와 소비자 간 합의된 키 (ex. roles, scope 등)

3. Signature

  • Header와 Payload가 위/변조되지 않았는지를 검증한다.

  • 서명 방식

    • 대칭키(HS256): 하나의 비밀키로 서명/검증 -> 단일 서버에 적합
    • 비대칭키(RS256/ES256): 발급자는 개인키로 서명, 검증은 공개키로 수행 -> 마이크로서비스/리소스 서버에 적합
  • 예시

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

📄 참고문서

  • 코드잇 강의 교안
profile
개발자로 성장하기

0개의 댓글