공부/서버

[공부/서버] CORS란?

오잎 클로버 2022. 2. 24. 09:15
728x90

웹 개발을 하다 보면 최소한 한 번 이상은 CORS 정책 위반으로 인해 에러가 발생해본 경험쯤은 있을 것입니다.

 

저 역시 저 혼자서 Spring & React를 공부해보고자 토이 프로젝트를 제작하는 도중 이 이슈를 처음 겪게 되었습니다.

당시, React, Spring 환경 둘 다 로컬 환경에서 진행하였고, Spring으로는 REST API를 먼저 완성시킨 후,

React로 API  서버와 통신을 시도했었습니다.

 

API 서버와 통신을 진행하여 데이터를 받아오면 되는 단순한 작업이었기에 아무 생각 없이 

통신을 진행했는 데, 콘솔에서 에러를 뱉어내어 꽤 당황하였던 경험이 있습니다.

(경험...)

 

당시 제 머리속

지금 보면 나름 친절하게 해결방법을 알려주는 에러 메시지라고 생각이 들지만, 웹 어플리케이션 개발 경험이 전무하던 당시에는 에러 메시지만 발생하면 머릿속이 하얘졌었고, 뭘 어떻게 해야 하는지 한참 헤맸던 기억이 있습니다.

... 그래서 CORS가 뭔데

이렇듯 우리가 겪는 CORS관련 이슈는 모두 CORS 정책을 위반하였기에 발생합니다. 개발하는 입장에서, 정책 때문에

신경 써야 하는 것들이 늘어나니 귀찮아질 수도 있지만, 사실 CORS라는 방어막이 존재하기에 이곳저곳에서 

가져오는 리소스가 안전하다는 최소한의 보장을 받을 수 있습니다.

 

CORS는 Cross-Origin Resource Sharing의 약자로 한국어로 직역하자면, 교차 출처 리소스 공유라고 해석할 수 있습니다.

교차 출처라는 것은 다른 출처를 의미하고, 즉 다른 출처 간의 리소스 공유 정책을 위반했다? 고 말할 수 있습니다.

그렇다면, 여기서 말하는 출처(Origin)의 의미는 무엇일까요.

출처(Origin)가 무엇인가요?

서버의 위치를 의미하는 URL들은 마치 하나의 문자열처럼 보여도, 사실은 여러 개의 구성 요소로 이루어져 있습니다.

이때 출처는 Protocol과 Host, 그리고 위 그림에는 나와있지는 않으나, :80, :3000 등과 같은 포트 번호까지 모두 합친 것을 의미합니다. 즉, 서버의 위치를 찾아가기 위해 필요한 가장 기본적인 것들을 합쳐놓은 것을 출처라고 합니다.

 

또한, 출처 내의 포트 번호는 생략이 가능합니다. 이는 각 웹에서 사용하는 HTTP, HTTPS 프로토콜의 기본 포트 번호가 정해져 있기 때문입니다. HTTP가 정의된 RFC 2616 문서에 상세히 작성되어있습니다. HTTP 기본 포트 번호는 80을 사용합니다.

 

그러나 만일 https://google.com:443과 같이 출처에 포트 번호가 명시적으로 포함되어있다면, 이 포트 번호까지 

모두 일치해야 같은 출처라고 인정합니다. 하지만 이 케이스에 대한 명확한 정의가 표준으로 정해진 것은 아니기에

더 정확히 이야기하자면, 어떤 경우에는 같은 출처, 어떤 경우에는 다른 출처라고 판단될 수 도 있습니다. 

그렇기에 브라우저의 개발자 도구의 콘솔에서 Location 객체가 가지고 있는 origin 프로퍼티에 접근함으로써 

손쉽게 애플리케이션이 실행되고 있는 출처를 알아낼 수 있습니다.

SOP이라는 정책도 있던데, 이 친군 뭔가요?

SOP은 Same-Origin Policy의 약자로, 말 그대로 같은 출처에서만 리소스를 공유할 수 있다 라는 규칙을 가진 정책입니다. 하지만 웹이라는 거대한 오픈 스페이스 환경에서 다른 출처에 있는 리소스를 가져와서 사용하는 일은 굉장히 흔한 일이기에 무작정 막을 수 도 없는 노릇입니다. 그렇기에 몇 가지 예외 조항을 두고 이 조항에 해당하는 리소스 요청은

출처가 다르더라도 허용하기로 하였습니다. 그중 하나가 CORS 정책을 지킨 리소스 요청입니다.

 

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

 

즉, 이렇게 다른 출처의 리소스를 사용하는 것을 제한하는 행위는 하나의 정책만으로 결정된 사항이 아니라는 의미가 되며, SOP에서 정의된 예외 조항과 CORS를 사용할 수 있는 케이스들이 맞물리지 않을 경우에는 아예 리소스 요청을 할 수 없는 케이스도 존재할 수 있게 됩니다.

 

하지만 굳이 이렇게 귀찮은 정책을 만들어서 개발자들을 괴롭히는 것일까?

어차피 개발자들은 정해진 서버 하고만 통신을 하도록 애플리케이션을 작성할 텐데...

중요하니까 만들었겠지..?

사실, 잘 생각해보면 출처가 다른 두 개의 애플리케이션이 마음대로 소통할 수 있는 환경은 꽤 위험한 환경입니다.

애초에 클라이언트 애플리케이션, 특히 웹에서 돌아가는 클라이언트 애플리케이션은 사용자의 공격에 너무나도 취약한 친구들이라는 것은 잊어서는 안 됩니다. 당장 개발자 도구만 열어도 DOM이 어떻게 작성되어있는지, 어떤 서버와 통신하는지, 리소스 출처 등등 각종 정보들을 아무런 제재 없이 열람할 수 있습니다.

 

최근에는 JS 소스코드가 난독화가 되었다지만, 난독화는 암호화가 아닙니다. 그리고 소스 코드를 직접 볼 수 있다는 것 자체만으로도 보안적으로 취약한 부분입니다.

심지어 아직까지도 소스 코드의 난독화가 안되어 개발자 도구만 열면 <script> 태그 안에 날 것 그대로의 소스 코드가 떡하니 노출되는 사이트들도 많다.

 

이런 상황 속에서 다른 출처의 애플리케이션이 서로 통신하는 것에 대해 아무런 제약도 존재하지 않는다면, 악의를 가진 사용자가 소스 코드를 쓱 구경한 후 CSRF(Cross-Site Request Forgery) XSS(Cross-Site Scripting)와 같은 방법을 사용하여 여러분의 애플리케이션에서 코드가 실행된 것처럼 꾸며서 사용자의 정보를 탈취가 쉽게 이루어집니다.

 (그리고 개발자가 신경 써야 할 일은 더 많아진다…)

같은 출처와 다른 출처의 구분 방법

사실 두 개의 출처가 서로 같다고 판단하는 로직 자체는 굉장히 간단한데, 두 URL의 구성 요소 중 Scheme, Host, Port, 이 3가지만 동일하면 된다.

https://workshop-6349.tistory.com:80라는 출처를 예로 들면 https:// 이라는 스킴에 workshop-6349 호스트를 가지고 :80번 포트를 사용하고 있다는 것만 같다면 나머지는 전부 다르더라도 같은 출처로 인정이 된다는 것이다.

URL 같은 출처 여부 이유
https://workshop-6349.tistory.com/help O 스킴, 호스트, 포트 동일
https://workshop-6349.tistory.com/help?lang=kr O 스킴, 호스트, 포트 동일
http://workshop-6349.tistory.com X 스킴이 다름
https://chopshop-6349.tistory.com X 호스트가 다름
https://workshop-6349.naver.com X 호스트가 다름
https://workshop-6349:8000 ? 브라우저의 구현에 따라 다름

출처 비교 시 포트 번호를 완전 무시하는 브라우저는 Internet Explorer 밖에 없다. 그러니 이제 그만 관짝으로 보내주도록 하자

위 정보들 외에도 어떻게 해결하는지, 그리고 작동원리와 관련된 정보들이 존재하나 글이 길어질 것을 고려하여 이만 마치도록 하겠습니다.

 

 

이상입니다.