본문 바로가기
시큐어코딩/JAVA

[시큐어코딩]Java에서 CSRF(크로스사이트요청위조) 취약점 완벽 대응 가이드

by ICT리더 리치 2025. 4. 9.

웹 보안에서 CSRF(Cross-Site Request Forgery)는 사용자가 의도하지 않은 요청을 실행하도록 유도해, 권한 탈취 및 시스템 변경을 유발할 수 있는 치명적인 공격 방식입니다. 이번 포스팅은 Spring Boot 기반 환경에서 CSRF 공격이 어떻게 발생하는지, 그리고 이를 실무에서 어떻게 방어할 수 있는지를 중점적으로 다룹니다.

안녕하세요, ICT Leader입니다. CSRF는 로그인된 세션을 활용해 사용자의 권한을 도용하는 공격으로, 보안 설정이 기본으로 포함된 Spring Security 환경에서도 종종 비활성화되거나 잘못된 설정으로 인해 문제가 발생합니다. 이 글에서는 CSRF 공격의 원리, 취약한 코드 예제, 그리고 Spring Security 기반의 안전한 설정법까지 보안 전문가와 개발자가 모두 실무에 적용 가능한 내용을 중심으로 상세하게 설명드리겠습니다.

1. CSRF란 무엇인가?

CSRF(Cross-Site Request Forgery)는 사용자가 로그인한 상태를 이용해, 의도하지 않은 요청을 강제로 수행하도록 만드는 공격입니다. 예를 들어 사용자가 로그인 중인 은행 페이지에서 공격자가 만든 요청을 실행하도록 유도하면, 계좌 이체, 비밀번호 변경 등의 민감한 요청이 실행될 수 있습니다.

Spring Boot 애플리케이션에서 CSRF 보호를 적용하지 않으면, 세션 기반 인증 환경에서 로그인된 사용자 권한이 탈취되는 심각한 문제가 발생할 수 있습니다. Form 기반 POST 요청이 많은 서비스일수록 반드시 대응이 필요합니다.

2. CSRF 방어 없는 구조 – 취약한 코드 예제

아래 코드는 Spring Boot + Spring Security를 사용하는 웹 애플리케이션에서 CSRF 보호 설정이 비활성화된 상태로, 공격에 노출될 수 있는 위험한 구조입니다. 실무에서 테스트용 설정을 유지한 채 배포할 경우 보안 사고로 이어질 수 있습니다.


// 보안 설정 클래스
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf().disable() // ❗ CSRF 비활성화
      .authorizeRequests()
      .antMatchers("/**").permitAll();
  }
}

이 설정은 모든 요청에 대해 인증 없이 접근을 허용하며, CSRF 방어가 완전히 해제된 상태입니다. 이런 구성에서는 공격자가 사용자의 세션을 도용해 POST 요청을 강제로 실행할 수 있습니다.

3. Spring Security로 안전한 코드 구현

Spring Security는 기본적으로 CSRF 공격에 대한 방어 기능을 제공합니다. 하지만 개발 환경에서는 종종 csrf().disable()을 사용하거나, AJAX 요청 환경에서 의도치 않게 보호 기능이 비활성화되는 경우가 많습니다. 이렇게 잘못된 설정이 그대로 운영 환경에 반영되면, 사용자 세션을 도용하는 위조 요청이 가능해집니다. CSRF는 사용자가 의도하지 않은 요청을 인증된 상태에서 실행하게 만들기 때문에 단순한 설정 실수 하나로 기업 서비스 전체가 침해될 수 있습니다. Spring Boot 기반 시스템에서는 보안 설정을 조금 더 명확하고 구조화된 방식으로 작성하고, CSRF 토큰이 실제로 클라이언트와 정상적으로 주고받도록 확인해야 합니다.


// Web 보안 설정 클래스
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf(csrf -> csrf
        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // 쿠키로 토큰 발급
        .ignoringAntMatchers("/api/public/**") // 일부 경로는 제외
      )
      .authorizeRequests(auth -> auth
        .antMatchers("/admin/**").hasRole("ADMIN")
        .antMatchers("/user/**").authenticated()
        .anyRequest().permitAll()
      )
      .formLogin(form -> form
        .loginPage("/login")
        .defaultSuccessUrl("/dashboard", true)
      );
  }

  // CORS 정책을 병행 설정 가능
  @Bean
  public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedOrigin("https://trusteddomain.com");
    config.addAllowedMethod("*");
    config.setAllowCredentials(true);
    config.addAllowedHeader("*");
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", config);
    return source;
  }
}

이 설정은 CSRF 토큰을 쿠키로 발급하고, 일부 공용 API는 필터에서 제외되도록 구성합니다. 또한 관리자 및 사용자 경로를 분리하여 접근 제어를 강화하며, 로그인 성공 시 리디렉션, CORS 설정까지 함께 적용해 보안과 운영의 균형을 모두 만족하는 구성입니다.

4. 실무 Q&A – 토큰 방식과 보안 설정

Q. CSRF 토큰은 어떤 방식으로 전달되나요?

기본적으로 Spring Security는 토큰을 HTTP 쿠키 또는 요청 헤더를 통해 전달받습니다. HTML 폼에서는 숨겨진 <input type="hidden" name="_csrf">으로 전송되며, AJAX 요청의 경우 X-CSRF-TOKEN 헤더를 수동으로 설정해야 합니다.

Q. API 서버에서는 CSRF를 사용하지 않아도 되나요?

REST API 서버는 세션 기반 인증이 아닌 토큰 기반 인증(JWT 등)을 주로 사용하기 때문에 일반적으로 CSRF를 적용하지 않아도 됩니다. 하지만 세션 인증이 혼용되는 경우에는 반드시 검토가 필요합니다.

Q. 개발 시에는 csrf().disable()을 써도 되나요?

로컬 개발 환경에서는 임시로 비활성화할 수 있지만, 운영 환경에서는 절대 disable()을 그대로 두면 안 됩니다. 테스트 후 반드시 활성화 설정을 복구한 상태로 배포하세요.

Q. CSRF 토큰 유효 시간은 어떻게 관리하나요?

기본적으로 세션과 동일한 생명 주기를 따릅니다. 커스텀 토큰 저장소 구현을 통해 토큰 만료 시간을 명시적으로 설정하거나, 일정 시간 후 갱신되도록 구성할 수 있습니다.

5. 마무리 요약

CSRF는 사용자의 인증 상태를 악용해, 악성 요청을 실행하는 공격입니다. Spring Boot 환경에서는 기본적으로 CSRF 방어가 활성화되지만, 제대로 동작하려면 구조에 맞는 설정과 사용법을 이해해야 합니다.

  • 폼 기반 요청에는 CSRF 토큰 필수
  • Spring Security 기본 설정 확인 및 재정의
  • AJAX 요청 시 CSRF 헤더 수동 설정
  • REST API는 세션 기반 여부에 따라 적용 여부 판단
  • 운영 환경에서는 절대 csrf().disable() 상태로 배포 금지
반응형