KKanging

[SpringSecurity] Cors 와 CorsFilter 뜯어보기 본문

백엔드/SpringSecurity

[SpringSecurity] Cors 와 CorsFilter 뜯어보기

천방지축 개발자 2024. 5. 24. 19:46

CorsFilter

CorsFilter는 서버의 Cors 공격을 막기 위한 Fitler이다. 같은 Origin 이 아닌 출처에 대한 요청을 막기위함이다.

Cross-Origin이란?

위와 같이 프로토콜 + hostname + port 중에 하나라도 다르면 Cross Origin 모두 같으면 동일한 Origin이라고 칭한다.

옛날에는 서버 와 프론트 서버가 동일한 서버에 있는 경우가 많았으므로 위와 동일한 Origin 에 대한 요청만 허용하도록 해서 보안을 강화하였다.

하지만 요즘에는 API 서버로 많이 사용하기 때문에 프론트 서버와 백엔드 서버가 다른 경우가 많다. 이런 경우에는 대부분 백엔드 서버에서 특정 Origin을 명시적으로 허용해줘야 한다.

Origin : protocal + hostname + port

same Origin : Origin이 완전히 똑같은

백엔드 개발자가 해야하는 일

프론트 서버의 Origin은 Cross Origin이라고 하더라도 허용하는 설정을 해줘야함

CorsFilter 뜯어보기

CorsFilter는 GenericFilter 를 상속 받았다. 그런데 Security의 다른 Filter들은 GenericFitlerBean이나 OncePerRequestFilter 를 상속 받았다

  • 사실 OncePerRequestFilter도 GenericFitlerBean을 상속 받았기 때문에 무슨 차이일까

GenericFilter와 GenericFilterBean의 차이

GenericFilter는 스프링과의 상관없는 추상 클래스이다.

하지만 GenericFitlerBean은 스프링이 제공하는 추상 클래스이다. 그리고 스프링에서 제공하므로 당연히 스프링의 Bean 컨테이너 또한 사용이 가능하다.

위처럼 스프링의 의존적이어서 스프링 관련 기능을 사용할 수 있냐 없냐의 차이점이 있다.

 

    @Override
    public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
            final FilterChain filterChain) throws IOException, ServletException {
        if (!(servletRequest instanceof HttpServletRequest) || !(servletResponse instanceof HttpServletResponse)) {
            throw new ServletException(sm.getString("corsFilter.onlyHttp"));
        }

        // Safe to downcast at this point.
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // Cors 인지 체크
        CorsFilter.CORSRequestType requestType = checkRequestType(request);

        // Cors 체크의 결과에 따라 request 에 값 저장
        if (isDecorateRequest()) {
            decorateCORSProperties(request, requestType);
        }
        // Cors 체크의 결과에 따라 처리를 구현함
        switch (requestType) {
            case SIMPLE:
                // Handles a Simple CORS request.
            case ACTUAL:
                // Handles an Actual CORS request.
                this.handleSimpleCORS(request, response, filterChain);
                break;
            case PRE_FLIGHT:
                // Handles a Pre-flight CORS request.
                this.handlePreflightCORS(request, response, filterChain);
                break;
            case NOT_CORS:
                // Cors 통과
                this.handleNonCORS(request, response, filterChain);
                break;
            default:
                // Handles a CORS request that violates specification.
                this.handleInvalidCORS(request, response, filterChain);
                break;
        }

Cors 설정 구현하기

위와 같이 allowedOrigin이 CorsConfigurationSource 설정으로 부터 가지고 온다.

CorsFilter는 Security에서 제공하는 Filter가 아니지만 내장이 되어있어서 Security에 FilterChain 설정 시에 해도 되고 따로 Bean 등록으로 설정해도 된다.

Bean 등록으로 설정하기

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("<https://example.com>"));
    configuration.setAllowedMethods(Arrays.asList("GET","POST"));
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}
<http>
	<cors configuration-source-ref="corsSource"/>
	...
</http>
<b:bean id="corsSource" class="org.springframework.web.cors.UrlBasedCorsConfigurationSource">
	...
</b:bean>

Security에서 설정하기

  1. 비활
        http
                .cors((cors)->cors.disable());

2. 디폴트

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			// if Spring MVC is on classpath and no CorsConfigurationSource is provided,
			// Spring Security will use CORS configuration provided to Spring MVC
			.cors(withDefaults()) // 혹은 설정 안하기
			...
		return http.build();
	}
}

3. 특정 Origin 허용

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{

    http
            .cors((cors) -> cors
                    .configurationSource(apiConfigurationSource()));

    return http.build();
}

CorsConfigurationSource apiConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("<https://api.example.com>"));
    configuration.setAllowedMethods(Arrays.asList("GET","POST"));
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

참고 문헌

CORS :: Spring Security