TIL - 20251202

juni·2025년 12월 2일

TIL

목록 보기
195/317

1202 Spring Security 아키텍처 심화: 인증, OAuth2, CORS/CSRF


✅ 1. Spring Security 인증 아키텍처의 재구성

  • Spring Security의 인증(Authentication) 과정은 여러 컴포넌트가 유기적으로 협력하여 이루어집니다. 이 구조를 이해하면 커스텀 인증 로직을 구현하는 데 큰 도움이 됩니다.

➕ 인증 처리의 핵심 흐름

  1. AuthenticationFilter:

    • 사용자의 로그인 요청(e.g., /login)을 가장 먼저 가로채는 필터입니다.
    • 요청에서 아이디/비밀번호와 같은 인증 정보를 추출하여, 미인증 상태의 Authentication 객체(주로 UsernamePasswordAuthenticationToken)를 생성합니다.
    • 생성된 토큰을 AuthenticationManager에게 전달하여 인증을 위임합니다.
  2. AuthenticationManager:

    • 인증 요청을 처리할 총괄 책임자입니다.
    • 실제 인증 로직을 직접 수행하지 않고, 자신에게 등록된 여러 AuthenticationProvider들 중에서 현재 요청을 처리할 수 있는 적절한 Provider를 찾아 인증을 위임합니다.
  3. AuthenticationProvider:

    • 실질적인 인증 로직을 수행하는 주체입니다.
    • UserDetailsService를 통해 DB 등에서 사용자 정보를 가져오고, PasswordEncoder를 통해 비밀번호를 비교합니다.
    • 인증에 성공하면, 사용자 정보(Principal), 자격 증명(Credentials), 권한(Authorities)을 모두 담은 인증 완료 상태의 Authentication 객체를 생성하여 AuthenticationManager에게 반환합니다.
  4. SecurityContextHolder:

    • AuthenticationManager로부터 인증 완료된 Authentication 객체를 돌려받은 AuthenticationFilter는, 이 객체를 SecurityContextHolder에 저장합니다.
    • SecurityContextHolderThreadLocal을 사용하여 현재 스레드 내에서 인증 정보를 공유하므로, 이후 애플리케이션의 어느 곳에서든 현재 로그인된 사용자 정보에 접근할 수 있게 됩니다.
  • 핵심: 개발자가 주로 커스터마이징하는 부분은 UserDetailsService(사용자 정보를 가져오는 방법)와 PasswordEncoder(비밀번호를 비교하는 방법)입니다.

✅ 2. OAuth2와 OpenID Connect (OIDC)

  • OAuth2: "인가(Authorization)"를 위한 프로토콜입니다. 사용자가 특정 서비스(Client)에게, 자신의 계정이 있는 다른 서비스(Resource Server, e.g., Google)의 특정 자원(e.g., 프로필 정보)에 접근할 수 있는 권한을 부여하는 과정을 표준화한 것입니다.

    • OAuth2의 결과물은 Access Token이며, 이 토큰은 "자원에 접근할 수 있는 열쇠" 역할을 합니다.
  • OpenID Connect (OIDC):

    • OAuth2 위에 구축된 "인증(Authentication)" 계층입니다.
    • OAuth2가 "권한"에만 초점을 맞추는 반면, OIDC는 "사용자가 누구인지"에 대한 신원 정보를 표준화된 방식으로 제공합니다.
    • OIDC의 결과물은 ID Token (JWT 형식)이며, 이 토큰에는 사용자의 이메일, 이름 등 표준화된 신원 정보(Claims)가 포함되어 있습니다.
    • 우리가 사용하는 대부분의 소셜 로그인(Google, Kakao 등)은 실제로는 OAuth2와 OIDC를 함께 사용하는 것입니다.

✅ 3. CORS (Cross-Origin Resource Sharing) 심화

  • SOP (Same-Origin Policy): 브라우저가 다른 출처(Origin)의 리소스 요청을 차단하는 보안 정책.
  • CORS: 이 SOP에 대한 예외를 허용하기 위한 메커니즘.

➕ Preflight Request (사전 요청)

  • 단순한 GET 요청과 달리, POST, PUT, DELETE와 같은 "실제 요청(Actual Request)"을 보내기 전에, 브라우저는 먼저 OPTIONS HTTP 메서드를 사용하여 서버에 "이러한 요청을 보내도 괜찮은지"를 물어보는 사전 요청(Preflight Request)을 보냅니다.
  • OPTIONS 요청의 헤더에는 Access-Control-Request-Method (실제 요청의 메서드), Access-Control-Request-Headers (실제 요청의 헤더) 등이 포함됩니다.
  • 서버는 이 Preflight 요청에 대해, Access-Control-Allow-Origin, Access-Control-Allow-Methods 등의 헤더를 담아 응답해야 합니다. 브라우저는 이 응답을 보고, 실제 요청을 보낼지 말지를 결정합니다.
  • Spring Security의 CORS 설정은 이러한 Preflight 요청에 대한 응답을 자동으로 처리해줍니다.

✅ 4. CSRF (Cross-Site Request Forgery) 심화

  • CSRF: 사용자의 인증된 세션(쿠키)을 악용하여, 공격자가 의도한 악의적인 요청을 사용자의 의지와 상관없이 서버에 보내도록 위조하는 공격.

➕ CSRF 방어 전략

  1. Synchronizer Token Pattern (CSRF 토큰):

    • Spring Security의 기본 방어 전략. 서버는 사용자 세션과 연결된 예측 불가능한 토큰을 생성하여 클라이언트에게 전달합니다.
    • 클라이언트는 상태 변경 요청 시, 이 토큰을 요청에 포함시켜 보내야 합니다. 서버는 세션의 토큰과 요청의 토큰을 비교하여 유효성을 검증합니다.
  2. SameSite Cookie Attribute:

    • 최신 브라우저에서 지원하는 쿠키 속성으로, 다른 도메인에서 시작된 요청에는 쿠키를 보내지 않도록 제한할 수 있습니다.
    • SameSite=Strict 또는 SameSite=Lax로 설정하면 대부분의 CSRF 공격을 효과적으로 방어할 수 있습니다.
  3. Referer 헤더 검증:

    • 서버에서 요청의 Referer 헤더를 확인하여, 허용된 도메인에서 온 요청인지를 검증하는 방식. (단, Referer 헤더는 위조될 수 있어 보조적인 수단으로 사용)
  • Stateless API와 CSRF:
    • JWTAuthorization 헤더를 통해 전달하는 Stateless 인증 방식에서는, 브라우저가 인증 정보를 자동으로 보내지 않으므로 CSRF 공격에 대해 기본적으로 안전합니다.
    • 따라서 세션/쿠키를 사용하지 않는 REST API 서버에서는 일반적으로 CSRF 방어 기능을 비활성화(csrf().disable())합니다.
    • 단, JWT를 쿠키에 저장하여 사용한다면, CSRF 공격에 취약해질 수 있으므로 SameSite 속성 설정 등의 추가적인 방어책이 필요합니다.

📌 요약

  • Spring Security의 인증FilterAuthenticationManagerAuthenticationProvider 순서로 책임이 위임되며, 최종 인증 정보는 SecurityContextHolder에 저장됩니다.
  • OAuth2"인가"를, 그 위에 구축된 OIDC"인증"을 담당하며, 우리가 사용하는 소셜 로그인은 이 둘의 조합입니다.
  • CORS는 브라우저의 SOP를 해결하기 위한 메커니즘으로, 복잡한 요청 전에는 Preflight Request (OPTIONS)가 먼저 발생합니다.
  • CSRF는 인증된 세션(쿠키)을 악용하는 공격이며, Spring Security는 CSRF 토큰으로 이를 방어합니다. 단, 헤더 기반의 JWT 인증을 사용하는 Stateless API는 CSRF 공격으로부터 상대적으로 안전합니다.

0개의 댓글