CORS(Cross-Origin Resource Sharing) error

이정연·2023년 5월 17일
0

project034

목록 보기
4/10

한 도메인 또는 Origin이 다른 도메인을 가진 리소스에서 액세스 할 수 있게 하는 보안 메카니즘으로써 서버와 클라이언트가 정해진 헤더를 통해 서로 요청이나 응답에 반응할지 결정하는 방식이다.

동일출처정책?
동일한 출처의 리소스에만 접근하도록 제한하는것 동일출처라는 것은 프로토콜, 호스트명, 포트가 같다는 것을 의미한다.

문제

  • 프로젝트로 협업을 하다보니 AWS 의 EC2로 배포한 상태에서 해당 EC2 DNS 주소로 프론트 쪽 로컬에서 요청을 보냈는데, 아래와 같은 CORS error 가 발견되었다.

내가 배포한 서버의 주소는 포트가 8080인 주소인데, request하는 주소는 포트가 3000인 주소라 동일출처가 아니기에 일어나는 에러였다.

원인

  • SecurityFilterChain 내부의 http의 .sameOrigin()

  • SecurityFilterChain 내부의 CorsConfigurationSource 에서 CORS 정책

해결

  • 스프링시큐리티에서 지원하는 인증과 권한부여설정을 하기위해서는 SecurityConfiguration class를 사용하여 정의하게된다.

  • 그 중 SecurityFilterChain을 이용할 수 있는데, 이것은 HTTP보안설정을 구성할 수 있게해준다.

public class SecurityConfiguration{
	.
    .
    .
	@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .headers().frameOptions().sameOrigin() --------> (1)
                .and()
                .csrf().disable()
                //기본적으로 아무설정을 하지 않으면 csrf 공격을 받음 클라이언트로부터 CSRF 토큰을 수신 후 검증
                .cors()
                .and()
                //세션을사용하지 않도록 설정함
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .formLogin().disable()
                .httpBasic().disable()
                .exceptionHandling()
                .authenticationEntryPoint(new MemberAuthenticationEntryPoint())
                .accessDeniedHandler(new MemberAccessDeniedHandler())
                .and()
                .apply(new CustomFilterConfigurer())
                .and()
                .authorizeHttpRequests(autorize -> autorize
                        .antMatchers(HttpMethod.PATCH, "/members/**").hasRole("USER")
                        .antMatchers(HttpMethod.GET, "/members").hasRole("ADMIN")
                        .antMatchers(HttpMethod.GET, "/members/**").hasAnyRole("USER", "ADMIN")
                        .antMatchers(HttpMethod.DELETE, "/members").hasRole("USER")
                        .antMatchers(HttpMethod.POST, "/blogs").hasRole("USER")
                        .antMatchers(HttpMethod.POST, "/questions").hasRole("USER")
                        .antMatchers(HttpMethod.POST, "/blogs/answer").hasRole("USER")
                        .antMatchers(HttpMethod.POST, "/questions/answer").hasRole("USER")
                        .anyRequest().permitAll()//JWT 적용전 우선 허용
                );
        return http.build();
    }
  • Spring Security에서는 Clickjacking 공격을 막기 위해 기본적으로 frameOptions() 기능이 활성화되어 있으며 디폴트 값은 DENY이다.
  • (1)과 같이 .frameOptions().sameOrigin()을 호출하면 동일 출처로부터 들어오는 request만 페이지 렌더링을 허용하게된다.
    따라서 배포시에는 해당 코드를 주석처리하던지, .sameOrigin()대신에 .disable()를 해줘야 cors에 걸리지 않게 된다.

  • 또한, 추가적으로 SecurityConfiguration class 내부에 구체적인 CORS 정책을 설정해준다.
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOriginPattern("*"); ----------->(1)
        configuration.setAllowedHeaders(Arrays.asList("*")); --------------->(2)
        configuration.setExposedHeaders(Arrays.asList("*")); --------------->(3)
        configuration.setAllowCredentials(true); ------------------->(4)
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PATCH", "DELETE"));//해당 메서드허용
        configuration.setExposedHeaders(Arrays.asList("Authorization", "Refresh")); -------------->(5)

        //CorsConfigurationSource 인터페이스의 구현클래스임
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        //앞에서 구현한 CORS 정책 적용
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }

위에서 사용한 메서드들은 다음과 같다.

  • addAllowedOriginPattern
  • setAllowedHeaders
  • setExposedHeaders
  • setAllowCredentials
  • setAllowedMethods
  • setExposedHeaders
  • (1) 특정 패턴 Origin만 허용해준다. 예를들면 "http://localhost:3000" 이런것들인데, "*"는 모든 Origin을 허용한다.
  • (2) 특정 Header CORS 요청 허용
  • (3),(5) 응답헤더 설정, 이것을 해주지 않으면 아래와 같이 응답 시 헤더에 토큰이 담기지 않는다.

  • 다음은 (5) 설정으로인해 헤더에 토큰이 담긴 경우이다.

authorization 과 refresh가 새로 노출된것을 확인 할 수 있다.

  • (4) 자격증명과 함께 요청 여부 (Authorization로 사용자 인증 사용 시 true)

추가

  • 검색하다 알게된 것
  • 프론트를 통하지 않고 내 로컬에서 CORS 확인 방법
  • Postman에서 test 할때 Headers에 Origin을 다음과 같이 추가해주고 요청을 보내면 에러가 난다면 CORS 응답이 오게된다.


ref : https://bohyeon-n.github.io/deploy/web/cors.html
https://oddpoet.net/blog/2017/04/27/cors-with-spring-security/

profile
반갑습니다.

0개의 댓글