Computer Science/Network

[HTTP] CORS Preflight

비소_ 2023. 1. 8.

CORS란?

CORS(Cross-Origin Resource Sharing)는 HTTP 헤더를 이용하여, 해당 자원에 접근할 수 있는 권한을 브라우저에게 알려주는 체제이다.

여기서 Origin(출처)프로토콜, 도메인, 포트번호를 묶어 말하는 것인데, 이 중 하나라도 다르면 다른 출처이다.

즉, CORS는 요청보낸 곳과 받는 곳의 출처가 다를 때, 이를 허용할지 말지 설정하는 것이라고 할 수 있다.

CORS는 브라우저와 서버 간의 안전한 교차 출처 요청 및 데이터 전송을 지원하므로 웹 기술에서 필수적이라고 할 수 있다.


CORS를 사용하는 요청 종류

  • XMLHttpRequest나 Fetch API 호출
  • 웹 폰트(CSS 내 @font-face에서 교차 도메인 폰트 사용 시)
  • WebGL 텍스쳐
  • drawImage()를 사용해 캔버스에 그린 이미지/비디오 프레임
  • 이미지로부터 추출하는 CSS Shapes

CORS Preflight

CORS Preflight는 요청을 실제로 보내기 전 서버에게 이 요청이 자원에 정상적으로 접근할 수 있는지 사전 조사(preflight)용으로 OPTIONS 메소드로 요청을 미리 보내는 것이다.

Preflight 요청은 브라우저가 자동으로 생성해 보내므로 따로 작성할 필요는 없다.

 

하지만, 일반적으로 단순한 요청들은 Preflight를 보내지 않는다.

이러한 요청들을 단순 요청(Simple Requests)라고 한다.


단순 요청(Simple Requests)

단순 요청은 다음 조건을 모두 충족해야 한다.

  1. 다음 중 하나의 메서드
    • GET
    • HEAD
    • POST
  2. 유저 에이전트가 자동으로 설정 한 헤더
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (아래의 값들만 허용된다)
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
  3. 요청에 사용된 XMLHttpRequestUpload 객체에는 이벤트 리스너가 등록되어 있으면 안된다.
  4. 요청에 ReadableStream 객체가 사용되지 않는다.

 

단순 요청 예시로는 다음과 같다.

const xhr = new XMLHttpRequest();
const url = 'https://bar.other/resources/public-data/';

xhr.open('GET', url);
xhr.onreadystatechange = someHandler;
xhr.send();

브라우저는 아래와 같은 요청 헤더를 생성한다.

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example

요청 헤더의 Origin을 통해 https://foo.example에서 왔다는 것을 알 수 있다.

서버는 다음과 같은 응답을 보내준다.

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[…XML Data…]

Access-Control-Allow-Origin이 우리가 알고 싶은 CORS 응답 헤더이다.

현재 와일드카드(*)로 모든 Origin에 대해 요청을 허용하고 있으므로 브라우저는 리소스에 접근할 수 있다.


Preflight 요청

단순 요청 조건을 하나라도 만족하지 않은 (즉, CORS 비표준이 있는 경우) 요청은 Preflight 요청이 보내진다.

 

예시 요청을 살펴보자.

const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://bar.other/resources/post-here/');
xhr.setRequestHeader('Ping-Other', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<person><name>Arun</name></person>');

비표준 요청 헤더인 Ping-Other와 Content-Type이 단순 요청 조건 아닌 application/xml이다.

따라서, 브라우저는 OPTIONS 메소드로 서버에게 Preflight를 전송한다.

Preflight 요청 헤더에는 Access-Control-Request-Method와 Access-Control-Request-Headers가 추가되는데, 이것이 CORS 요청 헤더이다.

그에 대한 응답으로 서버는 Access-Control-Allow-Methods와 Access-Control-Allow-Headers, Access-Control-Max-Age를 보내준다.

 

이를 통해 브라우저가 실제 요청을 보낼 수 있는지 파악하게 된다.


Spring Security CORS 적용 예시

@Bean
public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOriginPattern("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");

    source.registerCorsConfiguration("/api/**", config);
    return new CorsFilter(source);
}

CorsConfiguration은 CORS 정책을 설정하고,

UrlBasedCorsConfigurationSource는 지정한 URL 패턴에 정책을 적용시킨다.

 

이외에도 인증정보를 포함한 요청역시 Preflight가 보내진다.

자세한 내용은 아래 사이트를 참고하자.

https://developer.mozilla.org/ko/docs/Web/HTTP/CORS#%ED%94%84%EB%A6%AC%ED%94%8C%EB%9D%BC%EC%9D%B4%ED%8A%B8_%EC%9A%94%EC%B2%AD

 

교차 출처 리소스 공유 (CORS) - HTTP | MDN

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라

developer.mozilla.org

 

댓글