본문 바로가기
Computer Science/Network

[Web] CORS 이슈 | setAllowOrigins Preflight Request

by 카프리썬 2021. 4. 14.
728x90

개발하다가 몇시간째 헤맸던 CORS 이슈..도대체 무엇이였나?!

 

나의 경우는 서버는 localhost:8080번에서 실행하고 있었는데, 리액트는 localhost:3000에서 실행하고 있었다.

나는 단순히 api서버와 통신해서 데이터를 받아오는거니까 별 문제 없다고 생각했는데 웹화면에서 CORS이슈를 만났다.

결론부터 해당오류의 해결방법은 이랬다.

위 : 수정전 아래:수정후

하지만 단순하게 문제와 해결로 넘어가는게 아니라 더 자세하게 알아보기로 했다.  


CORS란?

Cross Origin Resoure Sharing의 약자로 클라이언트와 서버의 오리진(origin)이 다를때 발생하는 이슈이다.

CORS관련 이슈는 모두 CORS정책을 위반했을때 발생하는 것이라고 한다. 

 

오리진(origin)이 무엇이길래?

서버의 위치를 나타나는 URL은 사실 아래처럼 여러개의 구성요소로 이루어져있다. 

이때 오리진은 앞에 프로토콜과 HOST 그리고, 프로토콜에 따른 포트번호까지 합친 것이다.

예를 들면 포트번호는 HTTP프로토콜-80, HTTPS-443일 것이다. 

즉, 서버의 위치를 찾아가기 위해 필요한 가장 기본적인것들을 합쳐놓은것.

흔히 출처라고 이해하면 될 것 이다.

크롬개발자도구에서 어플리케이션이 실행되고 있는 origin을 알 수 있다

SOP정책과 CORS정책

웹 생태계는 다른오리진으로 리소스를 달라고 요청할때 2가지 정책으로 기반으로 제한을 한다.

한가지는 지금 CORS, 다른한가지는 SOP(Same-Origin-Policy).

SOP는 '동일출처정책'으로 말그대로 같은 출처에서만 리소스를 공유할 수 있다는 규칙이다. 

 

하지만, 어떻게 웹에서 한가지 출처만 사용하냐..!!

다른출처에 있는 리소스를 가져와서 사용하는게 비일비재하니 조건부로 허용하겠다. 라며 생긴게 CORS정책이다.

그래서 출처가 다른 리소스를 사용하려면 적어도 CORS정책을 지켜야하는게 생긴것이다. 

CORS정책을 위반하게 되는 상황이 발생하면 외부서버에서 요청한 데이터를 브라우저에서 보안목적으로 차단한다.

 

우리가 다른 출처로 리소스를 요청한다면 SOP 정책을 위반한 것이 되고, 거기다가 SOP의 예외 조항인 CORS 정책까지 지키지 않는다면 아예 다른 출처의 리소스를 사용할 수 없게 되는 것이다. 

 

왜 이런 정책이 필요한거야??

출처가 다른 두개의 어플리케이션이 맘대로 소통하면 위험하니까.

클라이언트 특히 웹에서 돌아가는 클라이언트는 사용자의 공격에 너무나도 취약하다.

크롬 브라우저만 해도 개발자도구로 어떻게 만들어졌는지 <script>태그 안의 코드들까지 다 볼 수 있다. 

이런상태에서 통신까지 제약이 없다면 그냥 복제 천국일테니까.. 

 

브라우저를 바꾸면 해결될 수도 있다

사실 origin을 비교하는 로직은 브라우저마다 다르다.

같은 오리진으로 판단하는 기준은 간단하다. url의 구성요소중 host, post, 프로토콜이 같으면 된다. 

하지만 애매할때가 있는데 이럴떈 브라우저들의 독자적인 비교로직을 따라가게 된다. 

CORS를 위반하는지 아닌지는 브라우저 스펙에 따라서 다르다.

그래서 브라우저를 바꾸면 CORS이슈가 해결될 수 있다.

브라우저에 구현되어 있는 스펙에 따라 origin을 비교하기 때문이다. 

 

CORS의 동작방법

기본적으로 웹 클라이언트 어플리케이션이 다른 출처의 리소스를 요청할 때  HTTP프로토콜 사용해서 요청보냄

1. 브라우저 ---> 요청헤더 Origin : 요청을 보내는 출처는 A다 -> 서버

이때 브라우저는 요청 헤더에 Origin이라는 필드에 요청을 보내는 출처를 함께 담아보낸다.

2. 브라우저 <--- 응답헤더 Access-Control-Allow-Origin : 리소스접근 허용된 출처다 <-- 서버

이후 서버가 이 요청에 대한 응답을 할 때 응답 헤더의 Access-Control-Allow-Origin이라는 값에

“이 리소스를 접근하는 것이 허용된 출처”를 내려준다.

3. 브라우저 : 내가 보낸 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin 비교

이게 다르다면, cors정책을 위반했다고 판단하는 것이다. 

이후 응답을 받는 브라우저는 자신이 보냈던 요청의 Origin서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 이 응답이 유효한 응답인지 아닌지를 결정한다.

 

Preflight Request

기본흐름은 이러지만, cors가 동작하는 방식은 여러가지 시나리오가 있다. 그 중에서 preflight request를 찾아봤다.

내가 겪은 것 같기 때문이다.

Preflight는 브라우저가 요청을 보내기전에 보내는 예비요청이다.

이 예비요청에 option 메소드를 사용한다. 

즉, 본 요청을 보내기전에 브라우저 스스로 이 요청을 보내는 것이 안전한것인지 확인하는 작업이다. 

option으로 메소드로 예비요청을 보낸 후에, get메소드를 보낸다.

아마 그래서 내가 option메소드까지 만난것 같다. 

여기에서보면 request헤더의 origin은 localhost:3000이다.

반면, response 헤더의 access-control-allow-origin은 *(와일드카드) 표시로 전부허용하여 200이 떨어지고,

본요청인 post를 수행할 수 있게 된 것 같다.

CORS해결과정 중에 만난 OPTION 메소드

CORS를 해결한 방법

서버에서 Access-Control-Allow-Origin 헤더에 알맞은 값을 세팅해주는 것이다.

맨처음에서도 나왔듯이 setAllowedOriginPatterns("*")을 통해서 모든 출처에서 오는 요청을 받는다고 표시했다.

하지만 이는 보안적으로 심각한 이슈가 발생할 수 있으니, 직접 명시하는게 좋다고 한다.

 

나같은 경우는 이미 setAllowOrigins("*")로 Access-Control-Allow-Origin 헤더가 만들어져 있는 줄 알았다.

그런데 오류가 났던 info페이지에는 response header에 없었다. 근데 

CORS가 발생한 info페이지 -> response 헤더에 뭐가 없다

근데 메세지를 입력했을때 publish 페이지에는 있었다..뭐지..

하지만 chatting url이 cors오류로 화면엔 보낸 메세지가 나오지 않았따.

publish페이지는 발생하지 않음! -> response 헤더에 * 잇음

그래서 일단 오류메세지에서 권장해준 방법인 패턴식 setAllowedOriginPatterns("*") 으로 변경했따

CORS가 해결된 info페이지 -> response 헤더에 origin이 있다
앞에 CORS오류가 해결되니 publish페이지도 잘 나와서 입력한 메세지가 보였다

 

결론 

누군가는 그냥 setAllowedOrigins("*")로 해결했다고 하는데, 나는 왜 패턴으로 변경해야했을까? 는 아직 잘 모르겠다.

그래도 CORS가 뭐고, 어떻게 동작하는지도 알아보고

내 상황에 적용해서 request, response헤더까지 자세히 살펴볼 수 있었던 배움이였다.

 


출처

그냥 setAllowedOrigins("*")로 해결했다고 하던데.. www.4te.co.kr/881

간단하지만 친절했던 cors 이슈 velog.io/@wlsdud2194/cors
진짜 유익하고 유용했던 cors총정리 evan-moon.github.io/2020/05/21/about-cors/

반응형

$(document).ready(function() { var $toc = $("#toc"); $toc.toc({content: ".tt_article_useless_p_margin", headings: "h2,h3,h4"}); });