study/이거저거

엉터리 정리 - CORS

올스왑 2022. 5. 7. 23:49

인터넷을 돌아다니며 공부한 내용입니다. 틀린 내용이 있을 수 있으니, 혹시 있다면 댓글 부탁드립니다. 출처는 글 하단에 있습니다.

 

최근 cors를 접하게 된 기회가 생겼는데, 개인적으로는 "서버가 헤더에 요청할 수 있는 도메인을 추가해서 응답해주는 거" 정도로 알고 있었다. 왜 있는지도 몰랐다. 그런데 공부하다 보면서 생각보다 중요한 정책임을 알았다.

비흡연자이지만, 이름이 비슷해서 생각났다.

 

 

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

구글에 cors를 검색하면 mdn 문서에 나오는 정의이다.

 

문장들을 분석해보자. 먼저 " 출처에서 실행 중인 애플리케이션"이란 말이 등장한다. 애플리케이션은 알겠는데 " 출처"라는 단어가 무엇을 의미할까? 다행히 링크가 걸려있다.

웹 콘텐츠의 출처(origin)는 접근할 때 사용하는 URL의 스킴(프로토콜, 호스트(도메인), 포트로 정의됩니다. 두 객체의 스킴, 호스트, 포트가 모두 일치하는 경우 같은 출처를 가졌다고 말합니다.

편의상, 출처를 도메인이라고 생각하자.

 

쉽게 해석해보면 "cors 추가 http 헤더를 사용하여, 도메인에서 실행 중인 애플리케이션이 다른 도메인의 선택한 자원에 접근할 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. 애플리케이션은 리소스가 자신의 도메인과 다를 교차 출처 http 요청을 실행합니다."

 

나는 지금까지 사이트가 어느 도메인과 통신을 하던 막히는 경우가 있나? 생각했었다. 당연히 되는 거라고 생각했었다.(아마 프론트 공부를 제대로 안해서 그런 같다ㅠ) 그래서 이런 것이 필요한 것인지 등장 배경을 찾아 봤다.

 

요새 서비스는 프론트엔드 레이어와 백엔드 레이어가 구분되어 개발되어진다. 그러다 보니, 프론트에서 api 호출하는 경우, 애플리케이션이 실행 중인 도메인이 아닌 다른 도메인의 api 통신하게 되는 경우가 있다. 하지만 과거에는 서버와 브라우져가 단순히 html 문서 정도만 주고 주고받는 것이 대부분이었고 도메인이 다른 경우, 자원을 주고 받을 없는 것이 브라우져의 정책이었다.(SOP, Same Origin Policy) 다른 도메인 서버로 요청하는 것이 보안 악의적인 행위로 받아 들여졌었다.(예를 들어 해킹 서버에 접속하여 해킹 서버가 자바스크립트 등을 이용해서 브라우져가 기존 서버를 해킹할 수도 있다.) 따라서 같은 도메인이 아닌 곳으로 요청하는 것을 막았던 것이다.

 

그러나 서버와 클라이언트가 주고 받는 데이터의 종류가 다양해지면서 변화가 생겼다.

 

블로그 서비스를 예를 들어보자. 사용자는 블로그 서비스에 들어와 기존에 작성자에 의해 기록된 글을 있을 것인데 이것은 블로그 서버가 제공할 것이다. 그런데 블로그 서비스 제작자가 그 날의 스포츠 경기, 날씨 등을 보여주는 위젯을 추가하려고 한다. 물론 해당 데이터를 제공하는 다른 도메인 api 있다고 가정하자.  위젯을 보여주는 방법은 크게 2가지가 있을 것이다.

  1. 기존 서버가 스포츠 결과 api, 날씨 api 통신하여 클라이언트에게 제공한다.
  2. 클라이언트 브라우져가 직접 스포츠 결과 api, 날씨 api 통신해서 보여준다.

그러나 과거에 경우, 위에 설명한 이유로 2번의 방식을 사용할 없었다. 이런 한계를 극복한 방식 중에 jsonp라는 것이 있다. 서버에서 스크립트를 제공하는 것인데, 콜백 함수 등을 사용해서 브라우져가 다른 도메인 서버와 통신하게 하는 방식이다. 그러나 이것은 우회적인 방식이었기에 공식적으로 등장한 것이 cors이다.

 

다시 MDN에서 정의한 cors를 보자. Cors라는 것은 "추가 HTTP 헤더" 사용해서 다른 출처의 선택한 자원에 접근할 있는 권한을 부여한다고 한다. 그럼 어떤 HTTP 헤더를 사용해서 어떤 방식으로 권한을 주는 것일까?

 

cors를 사용하는 블로그 서버는 브라우져가 스포츠 api, 날씨 api 접근할 있도록 추가 http 헤더를 통해 브라우져를 허락해줄 것이다. 어떻게 허락할까? 서버가 클라이언트 브라우져에게 응답할 , "이런 이런 사이트는 접근해도 괜찮아"하는 출처들을 http 헤더에 추가하는 것이다. 그렇게 서버는 cors 허용해준다는 주소로 스포츠 결과 api, 날씨 api 등을 적고 응답해주면 브라우져는 해당 api 자원을 요청하는 것이 가능해진다.

 

구체적으로 보자. 크게 cors가 사용되는 시나리오를 3가지로 볼 수 있다.

 

먼저 프리플라이트 요청(Preflight Request)이다. 이것은 서버에 OPTIONS 메서드를 통해 다른 출처의 리소스에 요청이 가능하지 확인을 받는다. 여기서 클라이언트가 보내는 요청을 Preflight Request, 서버의 응답을 Preflight Response라고 한다. 그리고 가능하다는 응답을 받으면 실제 요청을 보내는 것이다. 마찬가지로 Actual Requset와 Actual Response라고 한다.

Preflight Requset에는 Origin(요청하려는 출처), Access-control-Request-Method(실제 요청에 쓸 메서드), Access-control-Request-Headers(실제 요청에 쓸 추가 헤더)를 보낸다. 이것들에 대해 서버에게 허락을 받는 것이다.

Preflight Response는 Access-control-Request-Origin(서버에서 허가한 출처), Access-control-Request-Methods(서버에서 허가한 메서드), Access-control-Request-Headers(서버에서 허가한 헤더), Access-control-Max-Age(Preflight 응답 캐시 기간, Preflight Response를 캐싱해놓을 수 있는 시간이다. Preflight 통신 수를 줄일 수 있다.)가 담겨 있다.

Preflight Response의 특징으로는 응답 코드가 200번대여야 하고, body는 비어있는 것이 좋다.

 

그다음은 단순 요청(Simple Request)이다. 이 경우 Preflight Request 없이 바로 본 요청을 보내면서 교차 출처인지 확인한다. 위 블로그 예시가 여기 해당할 것이다. 대신 다음 조건들을 맞춰줘야 한다.

요청할 때 쓰는 메서드는 GET, POST, HEAD 중에 하나여야 하고, Content-Type도 "application/x-www-form-urlencoded", "multipart/form-data", "text/plain"여야 하고, 헤더는 Accept, Accept-Language, Content-Language, Conten-Type 만 허용된다.

클라이언트가 서버에 요청을 보낼 때 Origin이라는 헤더 값을 추가해서 서버로 보내면, 서버는 허용된 출처들을 헤더(Access-Control-Allow-Origin)에 추가하여 응답해준다. 그리고 브라우저는 받은 정보를 바탕으로 허락된 출처라면 요청을   있게 된다.

그럼 이렇게 단순 요청을 하면 브라우저가 판단해서 한 번만 요청해도 되는데 왜 Preflight가 왜 필요할까? Cors 체제가 탄생 이전에 만들어진 서버를 위해서이다. 과거 서버들은 브라우저가 SOP request만 가능하다고 판단하고 만들어진 것이기 때문에 교차 출처 요청에 대한 처리를 할 수 없다 보니, 이런 서버들을 보호하며 교차 출처 요청을 가능하게 하기 위해서 preflight request를 사용하는 것이다.

 

마지막으로 인증정보 포함 요청(Credentialed Request)이다. 인증 관련된 헤더를 포함할 때 사용하는 요청이다. 이 때는 더 엄격하다. 클라이언트는 요청 메시지에서 "credentials"를 include 하고, 서버에서도 Access-Control-Allow-Credentials를 true로 설정한다.

그리고 인증정보 포함 요청에 대한 응답에서는 와일드를 카드를 사용할 수 없고 "보내는 곳의 출처 -  페이지 주소"를 정확히 명시해야 한다.


출처:

https://developer.mozilla.org/ko/docs/Web/HTTP/CORS

CORS(Cross-Origin Resource Sharing)이 나오게 된 배경 이야기

웹개발 짜증유발자! CORS가 뭔가요?

[10분 테코톡] 🌳 나봄의 CORS