본문으로 바로가기

CORS에 대하여

category 개발/프론트엔드 2023. 3. 9. 23:23

이번 글에서는 프론트엔드를 배우다 보면 쉽게 만나 볼 수 있는 유명한 문제인 CORS 이슈에 대해서 다뤄 볼 것이다.

 

분명 처음에 개발을 배울때나 프로젝트를 세팅할 때 한번씩 마주쳤던 기억이 있는 이슈인데, 그렇게 깊은 인상이 남았던 기억은 없는걸로 보아서 해결하는데 시간이 오래 걸리지는 않았던것 같다. 그도 그럴게 하도 유명한 이슈라, 온 인터넷에 공략 방법이 적혀있으니 말이다. 초보자 코스에서 만나는 보스몹인데 완전히 패턴 파악이 끝난 경우라고나 할까. 내게 특별한 기억은 없던 이슈이다.

 

하지만 프론트엔드에서 자원이란 개발할때 떼놓을 수 없는 것이다. CORS 이슈는 이미지나 파일과 같은 자원에 관한 이슈이므로 한번쯤 탐구해볼만한 가치가 있다고 생각한다. 이번 글에서는 CORS 이슈가 정확히 무엇인지, 왜 발생하는지, 이러한 이슈를 띄워야만 하는 이유는 무엇인지에 대해 알아보겠다.

CORS란?

CORS 에러 메시지는 웹 개발을 하다보면 한번쯤 마주치는 오류 메시지이다. 보통 처음에 api request를 배울때 axios나 fetch를 사용하게 될텐데, 분명 api 엔드포인트로 제대로 요청을 보냈는데도 이런 오류가 뜨기 때문에 당황하기 십상이다.

 

에러 문구를 읽어보다 보면 CORS Policy라는 단어가 눈에 띈다. 뭘 잘못했길래 이러는걸까. CORS란 Cross Origin Resource Sharing의 약자로, 직역하자면 교차되는 출처 자원들의 공유이다. 만약 로컬에서 외부 uri에 있는 자원을 요청할 경우, 다른 출처에 있는 자원을 요청하는 것이기 때문에 교차 출처 요청에 해당된다.

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

MDN

출처란?

CORS에 대해 이해하기 위해서는 먼저 출처의 개념에 대해 이해하고 있어야 한다. 출처란 우리가 웹을 사용하면서 항상 접하는 URL과 관련이 있다.

 

 

위의 그림은 웹사이트의 url을 구성요소에 따라 분해한 것이다. 여기에서 Protocol, Host, Port가 같다면 동일 출처(Origin)이라고 한다.

 

동일한 출처는 다음과 같다.

다른 출처는 다음과 같다.

여기서 후자인 다른 출처 요청에 해당될 경우, CORS 정책을 위반하게 되는 것이므로 오류가 발생하게 되는 것이다. 만약 정상적으로 응답을 받기 위해서는 정책에 따라 맞춰야만 한다.

왜 이런 번거로운 짓을 하는가?

이유는 단순하다. 보안상 위험하기 때문이다. 만약 출처가 다른 두 어플리케이션이 있을때, 이들이 자유롭게 통신할 수 있다면 꽤 위험한 환경이라고 할 수 있다. 왜냐면 A측 어플리케이션에서 악의적 의도를 품고 해킹을 시도한다고 했을때 방어책이 없기 때문이다. 이는 비유하자면 누군가 나의 핸드폰에 원하는 대로 앱을 설치하고, 실행할 수 있는 제어권을 주는것과 거의 동일한 수준으로 위험하다.

 

만약 CORS가 존재하지 않는다면 해커가 CSRF이나 XSS와 같은 방법을 사용해서 서버의 정보를 가로챌 수 있다. 다음은 SOP(동일 출처 정책)이 적용되지 않은 환경에서 해커가 악의적으로 홈페이지에 접근하는 상황을 가정한 것이다. SOP(Same Origin Policy : 동일 출처 정책)란 말 그대로 동일한 출처에서만 자원 공유를 허락하는 규칙이다. 즉 내 로컬 서버에 있는 이미지는 가져올 수 있지만 Pinterest에 있는 이미지는 가져올 수 없는 것이다.

 

 

  1. 사용자가 악성 사이트에 접속한다.
  2. 이때 해커가 몰래 심어놓은 악의적인 자바스크립트가 실행되어, 사용자가 모르는 사이에 어느 포털 사이트에 요청을 보낸다.
  3. 그럼 포털 사이트에서 해당 브라우저의 쿠키를 이용하여 로그인을 하거나 등 상호작용에 따른 개인 정보를 응답 값을 받은뒤, 사이트에서 해커 서버(hacker.example.com)로 재차 보낸다.
  4. 이외에도 사용자가 접속중인 내부망의 아이피와 포트를 가져오거나, 해커가 사용자 브라우저를 프록시처럼 악용할 수도 있다.

이러한 경우를 방지하기 위해 CORS와 SOP이 존재하는 것이다.

리소스(자원) 출처 비교하기

초보 개발자들이 많이 착각하는 부분중 하나가 아까 말했던 출처 구분 작업을 서버가 한다고 오해하는 것이다. 서버에 요청을 했더니 오류가 발생했기 때문에 그렇게 생각할 수 있다. 하지만 출처를 비교하는 작업은 서버가 아니라 브라우저가 수행한다.

 

서버에서는 제대로 요청을 받아서 데이터를 반환했지만, 브라우저에서 막혀서 CORS 이슈가 발생하는 것이다. 이를 해결하기 위해서는 예외 조항을 두어야 하는데, 그중 하나가 CORS 정책을 지킨 리소스 요청이다.

교차 출처 리소스 공유(Cross-Origin Resource Sharing)

CORS는 단어 그대로 다른 출처의 리소스 공유에 대한 허용/비허용 정책이다. 따라서 개발을 하면서 다른 출처간에 통신을 해야 하는 상황이 생겼을때, 예외로 CORS 정책을 허용하는 출처를 추가하라는 의미였던 것이다. 그렇다면 말을 좀 풀어서, 규칙을 추가해달라고 하면 얼마나 좋은가.

 

 

맥락 없이 HARAM POLICE 마냥 공포스러운 에러 메시지를 뿜어대니까 이렇게 악명이 높은거 같다는 생각도 든다. 좀더 친절하게 문구를 쓸 수도 있지 않았는가😤

 

CORS Policy 메시지의 내용을 요약하자면 SOP 정책을 위반하고 있으므로 CORS 정책에 따라 예외 처리를 하라라는 뜻이다. 그러면 예외처리만 하면 되는걸까?

브라우저의 CORS 동작 구조

위에서 말했듯, CORS의 여부는 브라우저가 판단한다. 그러니 클라이언트 측인 React 등에서 출처를 허용해줘야 한다고 생각하기 쉽다. 하지만 실제로는 서버측에서 출처(Origin)을 기재해줘야만 CORS를 우회할 수 있게 된다. 그렇다면 CORS의 동작 구조를 순서에 따라 알아가보자.

 

기본적으로 브라우저에서 서버로 요청을 보낼때는 HTTP 프로토콜을 사용하게 되는데, 이때 브라우저는 요청 헤더에 Origin이라는 필드에 출처를 담아서 보내게 된다.

 

 

보시다시피 Origin이 localhost로 지정되어 있는 모습을 볼 수 있다. 여기에서 출처는 프로토콜, 호스트, 포트가 모두 같아야 한다는 것을 명심하자. 이제 이 요청에 서버가 받고, 이에 대한 응답을 보낼 것이다.

 

 

이때 서버는 응답을 보낼때에 응답 헤더에 Access-Control-Allow-Origin이라는 필드를 추가하게 된다. 여기에 담겨있는 값은 이 리소스를 접근하는것이 허용된 출처 url이다.

 

이후 응답을 받은 브라우저는 자신이 보냈던 요청 헤더의 Origin과 서버가 보내준 응답 헤더의 Access-Control-Allow-Origin를 비교하여 차단해야 할지 말지를 결정한다. 만약 출처가 동일하지 않다면 CORS 오류가 나오게 되는 것이고, 만약 동일할 경우 해당 요청이 유효하다고 판단하게 되어 제대로 응답을 받을 수 있게 된다.

 

이번 글에서는 CORS이 무엇인지에 대해서 살펴보았다. 실제로 오류를 해결하는 방법은 다른 여러 블로그에서 볼 수 있으니, 이번 글에서는 CORS의 개념과 발생 원인 자체에 초점을 두고 글을 작성해보았다.

레퍼런스

WEB 📚 악명 높은 CORS 개념 & 해결법 - 정리 끝판왕 👏
CORS란 무엇인가?