티스토리 뷰

🙋‍♀️ 기술면접 대비

CORS

2023. 1. 18. 22:16

CORS

Cross-Origin Resource Sharing  :  교차 출처 자원 공유

 

 

 

1️⃣  출처(Origin)란?

 

웹에서 출처(origin)란 scheme(protocol), hostname(domain), 액세스에 사용되는 URL의 port로 정의된다.

 

-  스키마, 호스트 이름, 포트가 모두 일치하는 경우에만 동일한 출처를 가진다고 한다.

-  일부 작업은 동일 출처 콘텐츠로 제한되며 이 제한은 CORS를 사용하여 해제할 수 있다 .

 

 

일반적인 URL 예시를 살펴보자.

예시 URL의 구성요소는 아래 그림과 같다.

 

 

위 URL의 구조에서 Protocol, Host, Port가 같다면 같은 출처라고 취급한다.

 

 

자바스크립트로 location객체의 origin 속성을 찍어보면 출처를 확인할 수 있다.

console.log(location.origin);

 

 

 

 

 

 

2️⃣  SOP (Same-Origin Policy)  :  동일 출처 정책

 

한 출처(origin) 에서 로드한 문서 또는 스크립트가 다른 출처의 리소스와 상호 작용할 수 있는 방법을 제한하는 중요한 보안 메커니즘이다.

→  쉽게 말하면, 같은 출처인 경우에만 리소스를 받을 수 있도록 제한하는 정책이다.

 

이는 악의적인 웹사이트가 브라우저에서 JS를 실행하여 다른 사이트를 공격하는 등의 상황을 방지한다.

 

 

💡  CORS와 SOP가 나오게된 이유

웹의 보안취약점을 공격할 수 있는 CSRF, XSS 등의 방법을 막기위해 CORS와 SOP가 모든 http request에 적용되었다고 한다.

 

 

 

 

 

 

3️⃣  CORS 시나리오 3가지

 

request 요청에 Origin이라는 헤더가 있고, response에 Access-Control-Allow-Origin이라는 헤더가 있다.

이 두개의 헤더가 같으면 브라우저는 같은 출처라고 인식한다.

 

 

 

 

CORS가 동작하는 방식은 한 가지가 아니라 세 가지의 시나리오(Preflight Request, Simple Request, Credentialed Request))에 따른다.

 

 

 

▶  Preflight Request (예비 요청)

:  본격적인 교차 출처 HTTP 요청 전에 서버 측에서 그 요청의 메서드와 헤더에 대해 인식하고 있는지를 체크한다.

 

 

자바스크립트 fetch()를 사용하여 리소스를 요청하면 OPTIONS라는 메서드를 통해 예비 요청을 보내는데

이 때, 헤더에 요청에 대한 출처를 함께 실어 보낸다. 

 

예비 요청에는 Origin에 대한 정보 뿐만 아니라 Access-Control-Request-Headers, Access-Control-Request-Methode 등 예비 요청 이후에 보낼 본 요청에 대한 다른 정보들도 포함되어 있다.

 

서버는 Access-Control-Allow-Origin를 응답해주는데 브라우저에서 위 OPTIONS 메서드를 통해 보낸 출처와 Access-Control-Allow-Origin이 같다면 같은 출처라고 인식한다. 따라서, CORS 정책에 위반되지 않았다고 판단하여 본 요청을 보내 요청을 처리한다.

 

만약, 요청의 Origin과 응답의 Access-Control-Allow-Origin이 같지 않다면 브라우저는 CORS 정책을 위반했다는 에러를 발생시킨다. CORS 에러는 브라우저가 판단하는 것이라 서버에서는 정상적으로 200 응답을 보낸다.

 

 

 

 

▶  Simple Request

:  예비 요청을 보내지 않고 바로 본 요청을 하며 본 요청이 위의 예비 요청에서 한 행동을 모두 포함하고 있다. 위의 시나리오와 마찬가지로, Origin과 Access-Control-Allow-Origin을 비교해서 CORS를 위반했는지 판단한다.

 

 

Simple Request는 아래의 특정 조건을 만족하는 경우에만 사용할 수 있는데 이 조건들이 까다로워서 실질적으로 보기 힘든 경우라고 한다.

 

-  요청 메서드는 GET, HEAD, POST 중 하나여야 한다.

-  Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안된다.

-  만약 Content-Type을 사용하는 경우, application/x-www-form-urlencoded, multipart/form-data, text/plain만 허용된다.

 

JWT를 사용하는 경우 위의 두 번째 조건을 만족하지 못하고,

rest API를 사용하는 경우 application/json이 허용되지 않는 세 번째 조건을 만족하지 못한다.

 

따라서, Simple Request를 실질적으로 볼 일은 거의 없다.

 

 

 

 

 

▶  Credentialed Request

:  인증된 요청을 사용하는 방법으로, CORS의 기본적인 방식이라기 보단 다른 출처 간의 통신에서 보안을 조금 더 강화하는 방법이다.

 

fetch 등의 비동기 API는 기본적으로 쿠키를 담아서 요청을 보내지 않는다. 쿠키를 담으려면 아래와 같은 추가적인 옵션 값이 주어 서버에서 이 요청을 보낸 사람이 인증이 된 사용자인지 한번 더 검증을 한다.

옵션 값 설명
same-origin(기본값) 같은 출처 간의 요청에만 인증 정보를 담는다.
Include 모든 요청에 인증 정보를 담는다.
Omit 모든 요청에 인증 정보를 담지 않는다.

 

 

fetch안에 credentials라는 옵션을 주어 쿠키를 함께 담아 보내고, 서버에서는 인증이 완료되면 정상적인 응답을 보낸다. 마찬가지로 브라우저에서 CORS 정책을 위반하는지 판단한다.

 

Credentialed Request도 제약사항이 존재한다.

-  Access-Control-Allow-Origin에 *을 사용할 수 없으며, 명시적인 URL이어야 한다.

-  응답 헤더에는 반드시 Access-Control-Allow-Credentials: true가 존재해야 한다.

 

 

 

 

4️⃣  결론

 

CORS란 모든 출처의 자원을 받는 것은 보안 상의 위험이 있으니 원하는 출처의 자원만 받을 수 있도록 제한을 할 수 있는 정책이다.

 

Server Side에서는 응답 헤더에 올바른 Access-Control-Allow-Origin을 세팅해주어야 한다.

Client Side에서는 Webpack Dev Server로 리버스 프록싱하여 우회가 가능하다. 그러나 이러한 방법은 로컬환경에서만 가능하니 가장 좋은 방법은 서버 개발자에게 도움을 요청해야 한다.

 

 

 

 

 

 

 

참고 자료

🔗 [우아한테크]  럿고의 CORS

🔗 CORS는 왜 이렇게 우리를 힘들게 하는걸까?

🔗 [MDN]  CORS

🔗 [MDN]  Same-origin policy

🔗 [MDN]  Origin

🔗 [MDN]  Preflight request

 

반응형

'🙋‍♀️ 기술면접 대비' 카테고리의 다른 글

Currying과 Composition  (0) 2023.02.15
CSR, SSR, SSG, ISR  (0) 2022.09.27
브라우저 렌더링 과정  (0) 2022.09.26
프로필사진
개발자 삐롱히

프론트엔드 개발자 삐롱히의 개발 & 공부 기록 블로그