CORS란 무엇인가 (CORS, SOP, Preflight)

2022. 1. 4. 17:03프로그래밍

반응형

 

CORS 정책 위반...읽기 두려운 에러 메세지...

최근 인턴쉽에서 아비웹 고도화 프로젝트를 진행하면서 CORS 정책 위반으로 에러가 발생했었다.

강렬한 붉은색의 에러 메세지...

천천히 읽어보면 나름 친철하게 해결방법을 알려주고 있는데, 처음 봤을 때는 강렬한 붉은색의 에러 메세지에 당황했던 기억이 있다.

 

살펴 볼 키워드

CORS를 이해하기 위해서는 CORS라는 단어 뿐 아니라 연관된 용어들을 이해하고 알아야한다.
다음 순서대로 글을 작성하였다.

  • CORS (Cross-Origin Resource Sharing)
  • Origin (출처)
  • SOP (Same-Origin Policy)
  • Preflight

 

CORS (Cross-Origin Resource Sharing)

'다른 출처'에 리소스를 요청할 때, 해당 리소스에 접근할 수 있도록 HTTP header에 추가 설정을 하여 접근 권한을 부여하고 이를 브라우저에 알려주는 정책

  • https://example.com에서 실행되는 웹 애플리케이션이 https://api.example.com 의 특정 자원에 접근할 수 있는 권한을 HTTP header에 설정하여 접근 권한을 부여하도록 브라우저에 알려주는 정책이다.
  • CORS는 브라우저의 구현 스펙에 포함되는 정책이기 때문에, 브라우저를 통하지 않고 서버 간 통신을 할 때는 이 정책이 적용되지 않는다.
  • CORS 정책을 위반한 요청을 보냈을 시에도 서버는 이를 판단할만한 로직이 없기 때문에 정상 응답을 보냈다는 로그만 남아있게 된다.

 

Origin(출처)

Screenshot from 2022-01-04 14-45-04

출처는 scheme(protocol), host, port 까지 합친 것을 말한다.
즉, 출처가 같다는 것은 url에서 scheme, host, port 가 동일한 것을 의미한다.

Same-Origin인 경우

http://example.com/user/1
http://example.com/user/2
  • scheme, host 일치
  • http의 기본 포트는 80 이므로 포트도 일치 (https의 기본포트는 443)

Cross-Origin인 경우

1) scheme(http, https) 불일치

http://example.com
https://example.com

2) host 불일치

http://example.com
http://api.example.com

3) port 불일치

http://example.com:3000
http://example.com:8000

 

SOP (Same-Origin Policy)

웹 생태계에는 다른 출처로의 리소스 요청을 제한하는 것과 관련된 두 가지 정책이 존재한다.
한 가지는 CORS, 또 다른 한 가지가 SOP(Same-Origin Policy)이다.

SOP(Same-Origin Policy)란 말 그대로 '같은 출처'에서만 리소스를 서로 공유할 수 있는 정책이다.

그러나 웹 환경에서는 다른 출처에 있는 리소스를 가져와서 사용하는 일은 굉장히 흔한 일이다.
따라서 몇가지 예외 조항을 두고 이 조항에 해당하는 리소스 요청은 출처가 다르더라도 허용하기로 했는데, 그 중 하나가 "CORS 정책을 지킨 리소스 요청"이다.

우리가 다른 출처로 리소스를 요청한다면 SOP 정책을 위반한 것이 되나, SOP의 예외 조항인 CORS 정책을 지킨 리소스 요청이라면 다른 출처의 리소스를 사용할 수 있는 것이다.

 

CORS 동작 방식

  1. 브라우저에서 Header 객체의 Origin 속성에 요청을 보내는 출처를 담아 서버에게 요청을 보낸다.
  2. Origin: https://example.com
  3. 이후 서버가 이 요청에 대한 응답을 할 때, 응답 Header의 Access-Control-Allow-Origin 에 '리소스를 접근하는 것이 허용된 출처'를 담아 브라우저에게 응답을 보낸다.
  4. 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin 값을 비교한다.
  5. 두 개가 동일하다면 유용한 응답이라 판단한다.

 

기본적인 흐름은 위와 같지만, CORS가 동작하는 방식은 한 가지가 아니라 아래 세 가지 시나리오에 따라 변경된다.

  • Simple Request
  • Preflight Request
  • Credential Request

Simple Request

simple-request

 

  • 예비요청을 보내지 않고 바로 서버에게 본 요청을 보낸 뒤, 서버가 이에 대한 응답의 헤더에 Access-Control-Allow-Origin과 같은 값을 보내주면 그 때 브라우저가 CORS 정책 위반 여부를 검사하는 방식.
  • 프라플라이트와 단순 요청 시나리오는 전반적인 로직 자체는 같으나, 예비 요청의 존재 유무만 다르다.
  • simple request이 보내지는 조건 (조건이 까다로워 이 시나리오를 사용하는 경우는 거의 X)
    • GET, HEAD, POST 요청
    • Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안된다.
    • 요청 메서드가 POST일 때 Content-Typeapplication/x-www-form-urlencoded, multipart/form-data, text/plain만 허용된다.

 

Preflight Request

cors-preflight

 

  • 브라우저의 요청이 안전하고 유효한지를 확인하기 위해 브라우저가 본 요청을 보내기 전에 OPTIONS 메서드로 예비 요청(Preflight)을 보내는 방식.
  • preflight request이 보내지는 조건
    • GET, HEAD, POST 요청
    • Content-Type 헤더가 다음 값들인 경우 : application/x-www-form-urlencoded, multipart/form-data, text/plain
    • 요청에 사용된 XMLHttpRequest.upload 객체에 이벤트 리스너가 등록되어 있지 않을 때
    • ReadableStream 객체가 요청에서 사용되지 않을 때
  • 일반적으로 웹 어플리케이션을 개발할 때 가장 마주치는 시나리오이다.

Flow

  1. 자바스크립트의 fetch API를 사용하여 브라우저에게 리소스를 받아오도록 요청
  2. 브라우저는 서버에게 예비 요청을 먼저 보내고, 서버는 이 예비 요청에 대한 응답으로 현재 자신이 어떤 것들을 허용하고, 어떤 것들을 금지하고 있는지에 대한 정보를 응답 헤더에 담아서 브라우저에게 다시 보내주게 된다.
  3. 브라우저는 자신이 보낸 예비 요청과 서버가 응답에 담아준 허용 정책을 비교한다.
  4. 이 요청을 보내는 것이 안전하다고 판단되면 같은 엔드포인트로 다시 본 요청을 보내게 된다.
  5. 이후 서버가 이 본 요청에 대한 응답을 하면 브라우저는 최종적으로 이 응답 데이터를 자바스크립트에게 넘겨준다.

 

Credential Request

  • 인증된 요청을 사용하는 방식
  • CORS의 기본적인 방식이라기 보다는 다른 출처 간 통신에서 좀 더 보안을 강화하고 싶을 때 사용한다.
  • 기본적으로 브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequest객체나 fetch API는 별도 옵션 없이는 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다.
    인증과 관련된 정보를 요청에 담아 전달하고 싶다면 credentials 옵션을 사용하면 된다.
    • credentials의 값으로는 same-origin, include, omit 이 있다.include : 모든 요청에 인증 정보를 담을 수 있다.
    • ommit : 모든 요청에 인증 정보를 담지 않는다.
    • same-origin(기본값) : 같은 출처의 간 요청에만 인증 정보를 담을 수 있다.

 

참고 사이트

 

반응형