본문 바로가기
Computer Science/네트워크

[네트워크] CORS란? (CORS가 필요한 이유, CORS 에러 해결 방법 스프링, CORS 동작 흐름)

by 그적 2024. 3. 14.

목차

  • CORS란?
  • CORS가 필요한 이유 : SOP
  • CORS 에러 해결 방법
  • CORS 기본 동작 흐름
  • CORS 실제 동작 시나리오
    • Simple Request
    • Preflight Request
    • Credentialed Request

 


1. CORS(Cross-Origin Resource Sharing)란?

CORS는 Cross-Origin Resource Sharing의 약자로, 직역하면 동일 출처 리소스 공유 정책이다. 이것은 서로 다른 출처에 있는 리소스에 접근할 수 있도록 브라우저에게 권한을 알려주는 정책이며, 웹 애플리케이션이 자신의 출처와 다른 리소스를 사용할 때 HTTP Origin 필드에 자신의 출처를 담아 리소스 서버에 요청하는 것이다.

 

여기서 출처는 URL 경로를 의미한다. URL 경로는 프로토콜, 호스트, 포트 번호를 포함하고 있는데, 이 세 가지 구성요소가 모두 일치하다면 CORS를 신경 쓸 필요가 없다. 하지만 프로토콜, 호스트, 포트 번호 중 하나라도 일치하지 않는다면, CORS를 설정해 리소스 서버에 요청해야 한다.

 


2. CORS가 필요한 이유 : SOP(Same-Origin Policy)

CORS가 필요한 이유는 무엇일까? 그건 바로 애플리케이션이 SOP를 기반으로 통신하고 있기 때문이다.

 

SOP는 Same-Origin Policy의 약자로 동일한 출처에서만 리소스를 공유할 수 있는 정책이며, CORS와 반대되는 개념이다. SOP는 기본적으로 웹 보안을 강화하기 위해 설계되었는데, 다른 출처에서의 접근을 막아 악의적인 공격이나 정보 유출을 방지할 수 있다.

 

프론트엔드에서 백엔드로 API 요청을 보낼 때 흔하게 CORS 에러가 발생한다. 자바 스크립트에서는 기본적으로 서로 다른 출처의 리소스 공유를 제한하고 있으며, 하나의 컴퓨터 내에서도 프론트엔드와 백엔드가 설정된 포트 번호가 다르다. 따라서 프론트는 HTTP 헤더 Origin 필드에 출처를 담아 요청하고, 백엔드는 HTTP 헤더 Access-Control-Allow-Origin 필드에 허용하는 출처를 담아 응답해야 한다.

 


3. CORS 에러 해결 방법 (스프링)

CORS 에러는 서버의 HTTP 헤더  'Access-Control-Allow-Origin' 에 접근을 허용할 출처를 담아 응답함으로써 해결된다. 

 

1) Web MVC에서 CORS 설정

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedOrigins("http://localhost:3000") // 허용할 출처
            .allowedMethods("*") // 허용할 HTTP method
            .allowCredentials(true) // 쿠키 인증 요청 허용
            .maxAge(3000); // 원하는 시간만큼 pre-flight 리퀘스트를 캐싱
    }
}

 

2) 스프링 시큐리티에서 CORS 설정

@EnableWebSecurity
@Configuration
public class SecurityConfiguration {

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.cors();
    return http.build();
  }

  @Bean
  public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
    configuration.setAllowCredentials(true);
    configuration.setAllowedHeaders(Arrays.asList("*"));
    configuration.setAllowedMethods(Arrays.asList("*"));
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
  }
}

 


4. CORS 기본 동작 흐름

CORS를 확인하는 작업은 클라이언트 혹은 서버가 아닌 "브라우저"가 담당한다.

 

1) 클라이언트는 HTTP 요청을 보낸다.

 

2) 브라우저는 HTTP 요청 헤더, Origin 필드에 출처를 담아 요청한다.

 

3) 서버는 HTTP 응답 헤더, Access-Control-Allow-Origin 필드에 허용하는 출처를 담아 응답한다.

 

4) 브라우저는 요청의 출처와 응답의 출처를 비교하고, 만약 동일하다면 리소스를 가져오게 된다.

 

5) 클라이언트는 모든 리소스를 포함한 응답을 받게 된다.

 


5. CORS 실제 동작 시나리오

위의 CORS 기본 동작 흐름으로 CORS가 동작하는 과정을 이해할 수 있었을 것이다. 하지만 실제 웹서비스가 동작할 때 쿠키와 토큰과 같은 인증 데이터를 다른 출처에서 가져와야 하는 여러 상황이 발생할 수 있다. 따라서 다양한 CORS 실제 동작 시나리오를 보면서 어떤 상황에서 언제 CORS 인증이 이뤄져야 할지 파악해야 할 필요가 있다.

 

1) Simple Request (단순 요청)

CORS의 기본 동작 흐름과 동일하게 동작한다. 브라우저는 요청의 Origin 필드와 응답의 Access-Control-Allow-Origin 필드를 비교하여, CORS 위반 여부를 확인한다. 단순 요청의 경우에는 특정 조건을 충족하는 경우에만 동작한다.

 

  • 단순 요청은 아래 조건을 충족할 경우에만 동작한다.
  • 요청 메서드가 GET, POST, HEAD 중 하나여야 한다.
  • Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더 필드를 사용하면 안된다.
  • Content-type을 사용하는 경우에는 application/x-www-form-urlencoded, multipart/form-data, text/plain만 허용된다.

 

2) Preflight Request (예비 요청)

브라우저는 요청을 보내기 전, 예비 요청을 먼저 보내 서버와 통신이 되는지 확인한다. 예비 요청은 HTTP 메서드의 OPTIONS를 사용하며, 백엔드는 예비 요청이 들어올 수 있도록 HTTP 메서드의 OPTIONS를 허용해야 한다.

 

  • 예비 요청은 서버가 안전한지 확인한다.
  • 예비 요청은 HTTP 메서드의 OPTIONS를 사용한다.
  • 단순 요청이 발생하는 조건 때문에, 일반적인 fetch는 예비 요청으로 이뤄진다.

 

 

3) Credentialed Request (인증된 요청)

다른 출처 간의 통신에서 보안을 강화하고 싶을 때 사용한다. 만약 백엔드 서버의 Access-Control-Allow-Origin 필드가 모든 출처를 허용한다면, 프론트엔드 이외의 https://www.google.com 혹은 https://www.naver.com과 과 같은 사이트에서도 백엔드에 접근할 수 있게 된다. 따라서 백엔드에서 Access-Control-Allow-Origin을 프론트엔드 출처만 허용해 줌으로써 제한하고, 브라우저는 이러한 출처 명시가 명확한지, 인증된 사용자인지를 확인하는 작업을 추가로 진행해준다.

 

  • 클라이언트는 credential 옵션을 활성화한다.
  • 브라우저는 요청 헤더 Cookie 필드에 세션 ID를 담거나 Authorization 필드에 토큰을 담아 전달한다.
  • 서버는 응답 헤더 Access-Control-Allow-Origin-Credentials 필드를 true로 설정한다.
  • 서버는 응답 헤더 Access-Control-Allow-Origin-Origin 필드에 와일드카드(*)를 사용할 수 없다.
  • 서버는 응답 헤더 Access-Control-Allow-Origin-Methos 필드에 와일드카드(*)를 사용할 수 없다.
  • 서버는 응답 헤더 Access-Control-Allow-Origin-Headers 필드에 와일드카드(*)를 사용할 수 없다.

 

댓글