새소식

프론트엔드 공부/Web Server

SOP, CORS

  • -

들어가기 앞서 '출처'에 대해 알고 갑시다!


URL 구조

다른 출처의 출처가 무엇인지 살펴봐야 하는데, 출처가 무엇인지 알기 위해서 먼저 URL의 구조를 살펴보아야 합니다. URL 구조는 아래 그림과 같습니다.

프로토콜의 HTTP는 80번, HTTPS는 443번 포트를 사용하는데, 80번과 443번 포트는 생략이 가능합니다.

출처(Origin)란?

출처(Origin)란 URL 구조에서 살펴본 Protocal, Host, Port를 합친 것을 말합니다. 브라우저 개발자 도구의 콘솔 창에 location.origin를 실행하면 출처를 확인할 수 있습니다.

같은 출처 VS 다른 출처

같은 출처인지 다른 출처인지 이해를 돕기 위해 예제를 하나 살펴보도록 하겠습니다. 현재 웹페이지의 주소가 https://gjy0605github.io/tech/일 때 같은 출처인지 다른 출처인지 아래 테이블과 같은 결과를 얻을 수 있습니다.

https://gjy0605github.io/about 같은 출처 Protocal, Host, Port 동일
https://gjy0605github.io/about?q=work 같은 출처 Protocal, Host, Port 동일
https://gjy0605github.io/about#work 같은 출처 Protocal, Host, Port 동일
http://gjy0605.github.io 다른 출처 Protocal 다름
https://gjy0605github.io:81/about 다른 출처 Port 다름
https://gjy0605heroku.com 다른 출처 Host 다름

SOP (Same-Origin Policy의 줄임말로, 동일 출처 정책을 뜻합니다.)

 

SOP란 직역하자면 동일 출처 정책이란 뜻으로 동일한 출처의 리소스만 상호작용을 허용하는 정책입니다. 역으로 설명드리면 A 출처에서 온 문서가 B 출처에서 가져온 리소스와 상호작용하는 것을 차단하는 보안 정책입니다. 두 URL의 프로토콜, 호스트, 포트가 모두 같아야 동일한 출처로 인정되며 웹 사이트를 샌드박스화 하여 잠재적인 보안 위협으로부터 보호해주는 정책 입니다.

SOP가 없는 상황에서 악의적인 JavaScript가 포함되어있는 페이지에 접속하는 상황을 가정하여 설명드리겠습니다. 사용자가 악성페이지에 접속하여 악의적인 JavaScript가 실행되면 사용자가 모르는 사이에 포털사이트인 potal.example.com에 임의의 요청을 보내고 그 응답 값을 해커의 서버로 재차 보내 사용자의 소중한 개인정보가 탈취되는 것입니다. 더 나아가서는 JavaScript를 이용하여 사용자가 접속중인 내부망의 아이피와 포트를 스캐닝 하거나, 해커가 사용자 브라우저를 프록시처럼 사용할 수도 있을 것 입니다.

위의 몇 가지 사례를 통해 SOP가 존재하는 이유에 대해서 간접적으로 알아보았습니다. 따라서 이런 경우가 발생할 가능성을 사전에 방지하기 위해 SOP가 존재한다고 할 수 있습니다.

한 마디로 ‘같은 출처의 리소스만 공유가 가능하다’라는 정책인데요. 여기서 말하는 ‘출처(Origin)’는 다음과 같습니다.

출처는 프로토콜, 호스트, 포트의 조합으로 되어있습니다. 이 중 하나라도 다르면 동일한 출처로 보지 않습니다.
동일 출처 정책은 잠재적으로 해로울 수 있는 문서를 분리함으로써 공격받을 수 있는 경로를 줄여줍니다. SOP을 통해 해킹 등의 위협에서 보다 더 안전해질 수 있다는 것인데요. SOP은 애초에 다른 사이트와의 리소스 공유를 제한하기 때문에 로그인 정보가 타 사이트의 코드에 의해서 새어나가는 것을 방지할 수 있습니다. 이러한 보안상 이점 때문에 SOP은 모든 브라우저에서 기본적으로 사용하고 있는 정책입니다.


다른 출처의 리소스를 사용하게 될 일은 너무나도 많습니다. 당장 로컬 환경에서 개발을 할 때에도 클라이언트와 서버를 따로 개발하게 된다면 둘은 출처가 달라지게 됩니다. 어떻게하면 다른 출처의 리소스를 받아올 수 있을까요? CORS를 이용하면 됩니다.


CORS (Cross-Origin Resource Sharing의 줄임말로 교차 출처 리소스 공유를 뜻합니다.)

MDN에서는 CORS를 다음과 같이 정의하고 있습니다.

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

즉, 브라우저는 SOP에 의해 기본적으로 다른 출처의 리소스 공유를 막지만, CORS를 사용하면 접근 권한을 얻을 수 있게 되는 것입니다.

보안도 중요하지만 개발을 하다 보면 개발 시 기능상 어쩔 수 없이 다른 출처간의 상호작용을 해야 하는 케이스가 존재합니다. 이런 경우를 대비 하기 위해 SOP의 예외 정책으로 Cross-Origin Resource Sharing(이하 CORS)라는 정책을 마련해두었습니다. 따라서 CORS을 이용하면 SOP의 제약을 받지 않게 됩니다.

CORS 설정을 통해 서버의 응답 헤더에 ‘Access-Control-Allow-Origin’을 작성하면 접근 권한을 얻을 수 있습니다.


CORS 동작 방법

CORS의 동작 방식에는 크게 세 가지가 있습니다

1. 프리플라이트 요청 (Preflight Request):

클라이언트가 서버에 보안상 민감한 메소드 (예: PUT, DELETE)를 사용하거나, 특정 HTTP 헤더를 포함하는 요청을 하기 전에, 서버에서 요청을 처리할 수 있는지 여부를 확인하는 요청입니다. 서버는 이 요청에 대한 Access-Control-Allow-Methods와 Access-Control-Allow-Headers 헤더를 포함한 응답을 보내어 클라이언트에게 해당 요청을 처리할 수 있는지 여부를 알려줍니다.

프리플라이트 요청은 왜 필요한 걸까요?

  • 실제 요청을 보내기 전에 미리 권한 확인을 할 수 있기 때문에, 실제 요청을 처음부터 통째로 보내는 것보다 리소스 측면에서 효율적입니다.
  • CORS에 대비가 되어있지 않은 서버를 보호할 수 있습니다. CORS 이전에 만들어진 서버들은 SOP 요청만 들어오는 상황을 고려하고 만들어졌습니다. 따라서 다른 출처에서 들어오는 요청에 대한 대비가 되어있지 않았습니다.

2. 단순 요청 (Simple Request):

메소드가 GET, HEAD, POST이고, 특정 HTTP 헤더를 포함하지 않는 요청입니다. 이러한 요청에 대해서는 프리플라이트 요청 없이 바로 처리됩니다.

3. 인증정보를 포함한 요청 (Credentialed Request):

인증정보 (예: 쿠키, HTTP 인증)이 포함된 요청입니다. 클라이언트는 withCredentials 옵션을 true로 설정해야 합니다. 이렇게 설정하면, 클라이언트는 헤더에 포함된 인증정보를 같이 전송합니다. 서버는 Access-Control-Allow-Credentials 헤더를 true로 설정해야 하며, 이렇게 설정하면 서버는 클라이언트에서 전송한 인증정보를 수신할 수 있습니다.

예를 들어, 클라이언트가 GET 요청을 하고, 이 요청에는 쿠키가 포함되어 있습니다. 클라이언트는 withCredentials 옵션을 true로 설정하여, 쿠키를 같이 전송합니다. 서버는 Access-Control-Allow-Credentials 헤더를 true로 설정하여, 클라이언트에서 전송한 쿠키를 수신할 수 있는 상태로 유지합니다. 이와 같이, CORS 인증정보를 포함한 요청은 보안상 민감한 데이터(예: 로그인 정보, 결제 정보) 전송에 사용될 수 있습니다.


CORS 인증정보를 포함한 요청은 서버에서 쿠키 등의 인증정보를 같이 전송할 수 있게 해줍니다. 따라서, 클라이언트와 서버 간의 데이터 전송에서 보안을 강화할 수 있습니다.

하지만, CORS 인증정보를 포함한 요청은 브라우저에서 추가적인 처리가 필요하기 때문에, 단순 요청과 비교해 복잡하고, 처리 속도가 느릴 수 있습니다. 따라서, CORS 인증정보를 포함한 요청을 사용할 때는 적절한 사용에 유의하여야 합니다.

 

 

참고:
https://beomy.github.io/tech/browser/cors/

https://blog.zairo.kr/entry/%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%AC%EC%9A%B4-%EC%9B%B9-%EB%B3%B4%EC%95%88-%EB%AA%A8%EB%8D%B8-%EC%9D%B4%EC%95%BC%EA%B8%B0-1-SOP-CORS

https://en.wikipedia.org/wiki/Same-origin_policy

https://developers.google.com/web/fundamentals/security/csp?hl=ko

https://ko.wikipedia.org/wiki/자바스크립트

https://ko.wikipedia.org/wiki/웹_브라우저

https://ko.wikipedia.org/wiki/HTML

https://ko.wikipedia.org/wiki/마크_앤드리슨

https://en.wikipedia.org/wiki/Same-origin_policy

https://blog.lgcns.com/1165

https://wit.nts-corp.com/2019/02/14/5522

https://velog.io/@yejinh/CORS-4tk536f0db

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

https://developer.mozilla.org/ko/docs/Web/Guide/AJAX/Getting_Started

https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy

https://evan-moon.github.io/2020/05/21/about-cors/

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.