CORS

Chanhee Jang·2023년 3월 28일

Frontend

목록 보기
1/3

원인

브라우저에서 HTTP Request를 보낼 때, 같은 Origin (URL상의 protocol, domain, port)이 아니어서 브라우저쪽에서 에러남

서버간 통신에서는 CORS 에러가 나지 않는다.


Same Origin Policy

어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식입니다. 동일 출처 정책은 잠재적으로 해로울 수 있는 문서를 분리함으로써 공격받을 수 있는 경로를 줄여줍니다. - MDN

같은 출처의 리소스 요청만 허용하면 인터넷이라는 용어는 무용지물이 되고 말 것이라서 SOP에 일부 예외를 둔 것이 CORS이다.

Cross Origin Resource Sharing

한 출처에서 실행중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다. - MDN

보안상의 이유로 스크립트에서 다른 출처로의 HTTP 요청은 제한된다.

다른 출처로의 리소스를 불러오고 싶다면 올바른 CORS 헤더를 반환해야 된다.


CORS 되는 요청들

  1. XMLHttpRequest, Fetch
  2. Web font(CSS에서 @font-face 에서 다른 도메인 폰트 사용시)
  3. WebGL 텍스쳐
  4. drawImage 를 사용해 캔버스에 그린 이미지/비디오 프레임
  5. 이미지로부터 추출하는 CSS Shapes

CORS 동작 과정

CORS가 동작하는 시나리오는 총 3개다.

1. Preflight

브라우저가 서버로 HTTP Request를 보낼 때, 해당 Request 이전에 돌 다리를 두들기는 Request를 보낸다. 이를 Preflight (예비 요청)라고 부른다.

Preflight의 HTTP Method는 OPTIONS이다.

브라우저가 알아서 시행하는 Preflight 동작을 간략히 살펴보자.

  1. 스크립트의 fetch() 로 서버에게 리소스 요청을 한다.
    1. Access-Control-Request-Headers를 설정한다. (HTTP Request에 사용될 헤더)
    2. Access-Control-Request-Method 를 설정한다. (HTTP Request에 사용될 Method)
  2. 서버가 해당 요청에 대한 응답을 보내준다. 아래는 서버가 설정한 헤더 정보다.
    1. Access-Control-Allow-Origin (허용 가능한 Origin)
    2. Access-Control-Allow-Methods (허용 가능한 Method)
    3. Access-Control-Allow-Headers (허용 가능한 Headers)
    4. Access-Control-Allow-Max-Age (해당 Preflight가 브라우저에 캐싱될 수 있는 시간, 초 단위)
  3. 브라우저의 요청값, 서버의 응답값을 비교해 CORS 정책에 어긋나는지 아닌지 판단한다.
  4. 이제 브라우저가 실질적으로 서버에게 리소스 요청을 보낸다.

2. Simple Request

1번의 Preflight를 생략하고 바로 서버에 HTTP Request를 날리는걸 뜻한다.

서버가 Access-Control-Allow-Origin 헤더를 보내주면 브라우저가 CORS 정책을 검사한다.

Simple Request가 발동하려면 선제 조건이 필요하다.

  1. Request의 Method는 GET, HEAD, POST 중 하나
  2. Accept, Accept-Language, Content-Language, Content-Type, DPR, Save-Data, Viewport-Width, Width 헤더만 적용
  3. Content-Type 헤더가 application/x-www-form-urlencoded, multipart/form-data, text/plain 이여야 한다. 그게 아니면 1번 시나리오다.

3. Credentialed Request

클라이언트가 서버에게 Credential을 실어 요청할 때이다.

Credential은 보통 세션 ID가 저장된 쿠키, Authorization 헤더에 설정된 토큰 값이 있다.

클라이언트쪽에서 따로 옵션을 설정해준다.

same-origin, include , omit 이 있으며 fetch, axios 등등마다 옵션을 지정하는 법이 틀리다.

클라이언트가 3번 방법으로 HTTP Reqeust를 보내면 서버도 Response를 다음과 같이 지켜줘야 된다.

  1. Access-Control-Allow-Credential 을 true
  2. Access-Control-Allow-Origin 에 와일드 카드 불가능
  3. Access-Control-Allow-Methods 에 와일드 카드 불가능
  4. Access-Control-Allow-Headers 에 와일드 카드 불가능

해결법

프론트쪽에서 해결해야 되는 경우 프록시를 사용해야 된다.

vite, webpack의 개발 서버에 프록시 세팅을 할 수 있다.

실제 배포용은 따로 프록시 서버를 구축하자.

사실 서버에서 해결하는게 정석이다.

간단하다. Response해줄 때 HTTP 헤더를 설정해주면 된다.

  1. Access-Control-Allow-Origin (여기에 작성된 출처만 브라우저쪽에서 리소스 접근 가능)
  2. Access-Control-Request-Methods (허용할 HTTP 메소드)
  3. Access-Control-Allow-Headers (요청 허용 헤더)
  4. Access-Control-Max-Age (브라우저의 preflight 요청 결과 캐싱 설정)
  5. Access-Control-Allow-Credentials (자격증명 여부)
  6. Access-Control-Expose-Headers (원래 브라우저에게 노출되지는 않지만, 브라우저에게 접근 가능하게 해줄 헤더 설정)
profile
What is to give light must endure burning

0개의 댓글