HTTP_4. CORS

Seoyong Lee·2021년 5월 27일
0
post-thumbnail

CORS

Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the first resource was served.

교차 출처 리소스 공유(CORS)는 웹 페이지의 제한된 리소스를 첫 번째 리소스가 제공된 도메인 외부의 다른 도메인에서 요청할 수 있도록 허용하는 메커니즘이다.

Wikipedia - Cross-origin resource sharing

CORS(Cross-origin resource sharing)는 위의 정의와 같이 두 개 이상의 서버가 브라우저에 연결되는 것을 말한다. 여기서 중요한 점은 이러한 허락이 서버에서 예외적으로 허용된다는 점이다.

왜 이러한 기술을 사용하는 것일까?

SOP

CORS가 나오게 된 배경은 기본적으로 SOP(same-origin policy)와 관련이 있다. SOP는 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식으로 현재 대부분의 브라우저가 이를 따르고 있다. SOP는 다음 세 가지를 통해 출처를 확인한다.

  • protocol (ex) https:// )
  • domain (ex) developer.mozilla.org / 192.168.0.2 )
  • port (ex) 8080 )

그러나 React의 등장 이후 가상 돔을 이용한 SPA(Single Page Application)의 사용이 증가하면서 다른 출처의 리소스를 요청할 필요가 생기게 된다. 이를 해결하기 위한 방법이 바로 CORS로, SOP의 제한을 정해진 범위 내에서 느슨하게 만들어 다른 출처의 리소스를 사용할 수 있도록 허용해준다.

그렇다면 이러한 CORS는 필수로 사용해야 하는가?

프론트엔드와 백엔드로 나누어지는 아키텍처를 사용하는 것이 아니라면 CORS를 사용하는 것이 필수인 것은 아니다. 그러나 프론트 서버와 백엔드 서버를 동시에 이용해야 하는 웹 어플리케이션의 경우 CORS의 사용은 필수적이다.


출처 [React] 프론트 엔드와 백 엔드 분리 시 동작 원리 (vs 풀 스택)

웹 어플리케이션은 최초에 클라이언트가 요청을 보내면 준비한 HTML, CSS, JavaScript 파일을 프론트 서버에서 가져와 렌더링한다. 이러한 렌더링은 프론트 서버가 담당하며, 추가적으로 필요한 데이터만 백엔드 서버에서 가져오는 식으로 작동하게 된다. 만약 페이지의 일부만 변경되는 리렌더링이 발생하게 되면 해당 부분에 필요한 데이터만 백엔드 서버에 다시 요청하여 프론트 서버에서 리렌더링을 진행한다. 이러한 기능을 구현하기 위해서는 서로 다른 두 서버를 연결해야 하기 때문에 CORS 설정이 필요할 수 밖에 없다.

CORS의 request와 response

CORS 요청을 보내면 브라우저는 다음과 같은 방법으로 이를 처리한다.

Simple requests

다음과 같은 조건을 만족하면, 브라우저는 CORS 요청을 Simple request로 처리한다.

  • 다음 중 하나의 메소드 사용
    GET, POST, HEAD
  • Content-Type의 헤더 값으로 다음과 같은 값을 사용한 경우
    application/x-www-form-urlencoded
    multipart/form-data
    text/plain
  • XMLHttpRequestUpload 객체에 이벤트 리스너가 등록되지 않은 경우
  • 요청에서 ReadableStream 객체를 사용하지 않은 경우

이는 가장 단순한 요청으로 빠르지만 요청의 안정성을 판단하는 preflight request에 비해 보안상 취약하다.

Preflighted requests

Preflighted requests는 'Preflight' 요청을 서버에 먼저 보내 브라우저가 이를 판단하여 실제 응답을 보낼지 결정한다. 메소드는 OPTIONS를 사용하며 다음과 같은 요청 헤더를 사용한다.

  • Access-Control-Request-Method : 실제 요청에서 사용하는 메서드를 서버가 알 수 있도록 설정하는 헤더
  • Access-Control-Request-Headers : 실제 요청에 포함될 헤더를 서버가 알 수 있도록 설정하는 헤더
  • Origin : 요청을 보낸 출처로, URL 중 scheme과 host, port만 명시

브라우저는 위의 헤더와 응답의 Access-Control 헤더를 비교하여 실제 요청을 보낼지 판단한다. Preflighted requests는 실제 응답에 앞서 안전한지 검증하므로 Simple requests에 비해 보안이 강화된 방법이라고 볼 수 있다.

Credentialed requests

Credentialed requests는 인증정보(쿠키)를 함께 보내는 방식으로 다음의 두 단계를 통해 이루어진다.

  • 요청을 credentials 모드로 설정
fetch(url, {
  credentials: 'include'
}) 
  • 서버에서 응답 헤더로 Access-Control-Allow-Crendentials: true 설정

response

CORS 요청이 오면 서버는 Access-Control-* 헤더를 메시지에 포함시킬 수 있다.

  • Access-Control-Allow-Origin : 요청을 허용할 출처를 명시할 때 사용, *는 전체 허용
  • Access-Control-Allow-Methods : 어떤 메서드를 허용할 것인지 명시
  • Access-Control-Allow-Headers : 어떤 헤더들을 허용할 것인지 명시
  • Access-Control-Max-Age : preflight 요청에 대한 응답을 브라우저에서 얼마만큼 캐싱하고 있을지 설정
  • Access-Control-Expose-Headers : 브라우저가 스크립트에 노출시킬 헤더의 목록을 명시
    Access-Control-Allow-Credentials : Credentialed requests를 위한 헤더

서버는 위와 같은 헤더를 통해 리소스에 대한 CORS 요청을 어느 수준까지 허용할 것인지 브라우저에 알려준다.

참고
Wikipedia - Cross-origin resource sharing
MDN - Cross-Origin Resource Sharing (CORS)
CORS는 왜 이렇게 우리를 힘들게 하는걸까?
Authoritative guide to CORS for REST APIs
[React] 프론트 엔드와 백 엔드 분리 시 동작 원리 (vs 풀 스택)

profile
코드를 디자인하다

0개의 댓글