[Spring Security] CSRF 보호와 CORS

qufdl·2023년 8월 16일
0

Spring Boot

목록 보기
4/5
post-thumbnail

스프링 시큐리티에 자동 적용되는 CSRF 보호 때문에 HTTP POST를 구현할 땐 CSRF 보호를 비활성화 하는 보조 명령csrf().disable()을 추가해야 했다. CSRF 보호는 무엇이고, 이를 언제 이용해야 하는지 정리해 봤다.

애플리케이션에 CSRF(사이트 간 요청 위조) 보호 적용

CSRF란?

CSRF는 공격자가 사용자의 브라우저를 속여 사용자의 동의 없이 웹사이트에서 동작을 수행하는 웹 공격 유형이다.

사용자가 로그인 한 상태에서 알 수 없는 링크를 클릭했다고 가정하자. 이때 사용자의 브라우저가 페이지에 접근할 때 위조 스크립트를 받는다. 이제 스크립트는 로그인한 사용자 대신 작업을 행해 애플리케이션에 원치 않는 변경을 적용할 수 있다.

CSRF 보호는 어떻게 작동하는가?

  • CSRF 공격을 방지하기 위해 일반적으로 CSRF 토큰이 사용된다. CSRF 토큰은 서버 측에서 생성되며, 고유한 값을 갖는다.
  • CSRF 토큰은 변경 요청(POST, PUT, DELETE 등)과 함께 전송되며, 서버가 요청을 처리할 때마다 해당 요청에 포함되어야 한다.
  • HTTP 요청이 제출되면 Spring Security는 예상되는 CSRF 토큰과 요청에서 전송된 토큰을 비교한다.
  • 토큰 값이 일치하는 경우에만 요청이 처리되며, 일치하지 않으면 CSRF 공격으로 간주하여 요청을 거부한다.

CSRF 보호의 시작점은 필터 체인의 CsrfFilter다. 이 필터는 요청을 가로채어 변경 요청(POST, PUT, DELETE)일 경우 토큰이 포함된 헤더가 있는지 확인한다. 토큰을 포함하는 헤더를 추가하지 않거나 잘못된 토큰 값이 포함된 경우 애플리케이션은 요청을 수락하지 않는다.

구현 예제

맞춤형 필터 구현

@Slf4j
public class CsrfTokenLogger implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        Object object = request.getAttribute("_csrf");      //_csrf 요청 특성에서 토큰의 값을 얻고 콘솔에 출력함
        CsrfToken token = (CsrfToken) object;

        log.info("CSRF Token : {}", token.getToken());

        chain.doFilter(request, response);
    }
}
  • _csrf 요청 특성에서 CSRF 토큰의 값을 얻고 콘솔에 출력
  • CsrfFilter는 생성한 토큰 값을 요청 특성 _csrf에 저장함

구성 클래스에 맞춤형 필터 추가

@Configuration
public class ProjectConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.
                addFilterAfter(new CsrfTokenLogger(), CsrfFilter.class)+
                .authorizeHttpRequests()
                .anyRequest().permitAll();

        return http.build();
    }
}
  • CSRF 보호를 비활성화 하지 않음

위 처럼 구성하고 아래 엔드포인트를 호출한다

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String getHello() {
        return "Get Hello!";
    }

    @PostMapping("/hello")
    public String postHello() {
        return "Post Hello!";
    }
}
  • GET /hello : 정상적으로 호출 & CSRF 로그 행 확인됨
  • POST /hello : 403에러 발생

이처럼 CSRF 보호가 활성화되어있는 경우엔 변경 요청이 거부된다. POST 요청 시 CSRF 토큰 값을 포함해야 정상적으로 요청된다.

CSRF 보호는 서버 쪽의 세션이나 브라우저 쿠키를 사용하기 때문에 프론트엔드와 백엔드 서버가 동일할 때 사용하는 것이 좋다. REST API의 경우 HTTP와 같이 무상태(stateless)이기 때문에 CSRF 보호를 비활성화 해도 된다.

CORS(교차 출처 리소스 공유)란?

CORS는 웹 애플리케이션에서 다른 도메인의 리소스에 접근할 수 있는 권한을 부여하는 메커니즘이다.

웹 애플리케이션은 보안 상의 이유로 기본적으로 한 출처(origin)에서 로드된 문서나 스크립트가 다른 출처의 리소스와 상호 작용하는 것을 제한한다(= SOP : 동일 출처 정책). 예를 들어, http://example.com의 개발자가 http://example.org 소유자의 REST 엔드포인트를 호출하려고 하면 호출이 거부된다.

CORS 메커니즘으로 이 정책을 완화하고 서로 다른 출처 간의 요청을 허용한다. React나 Vue.js 등의 프레임워크로 개발한 프론트엔드 애플리케이션이 다른 도메인에서 호스팅하는 백엔드의 엔드포인트를 호출할 수 있는 것이다.

CORS 작동 방식

CORS를 이용하면 애플리케이션이 서로 다른 도메인 간의 호출을 가능하게 한다.

CORS메커니즘은 HTTP 헤더를 기반으로 작동하며, 가장 중요한 헤더는 다음과 같다

  • Access-Control-Allow-Origin : 도메인 리소스에 접근할 수 있는 외부 도메인을 지정
  • Access-Control-Allow-Methods : 다른 도메인에 접근할 HTTP 방식을 지정
  • Access-Control=Allow-Headers : 특정 요청에 이용할 수 있는 헤더에 제한을 추가

CSRF 보호와 CORS가 함께 언급되는 경우가 많은데, 이는 둘 함께 구현하면 웹 애플리케이션의 보안을 향상시킬 수 있기 때문이다. 둘은 각각 다른 웹 보안 측면을 다룬다.

Spring Boot에서 CORS 정책 적용

1. @CrossOrigin 어노테이션으로 CORS 정책 적용

컨트롤러의 메서드 위에 @CrossOrigin 어노테이션을 추가해 적용할 수 있다. 어노테이션의 allowedHeaders 특성과 methods 특성으로 허용되는 헤더와 메서드를 설정할 수 있다.

예시)

@RestController
     public class HelloController{
         @CrossOrigin(origins = "http://hello.com")
         @GetMapping("/api/data")
         public String getData() {
             // Controller logic
         }
     }

2. Configuration Class를 생성해 CORS 정책 적용

WebMvcConfigurer 인터페이스를 구현하는 Config 클래스를 생성해 적용할 수 있다.

@CrossOrigin을 사용할 경우 코드가 장황해지고 반복되는 코드가 많아질 것 같아 React와 협업하는 프로젝트에서 나는 이 방법을 사용했다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
                .addMapping("/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("OPTIONS", "GET", "POST", "PUT", "DELETE");
    }
}

3. Spring Security를 이용해 CORS 정책 적용

아직 프로젝트에 Spring Security를 적용해보지 않아서 추후 추가할 예정

Reference

  • 스프링 시큐리티 인 액션

1개의 댓글

comment-user-thumbnail
2023년 8월 16일

정보 감사합니다.

답글 달기