SOP(Same Origin Policy)와 CORS(Cross Origin Resource Sharing)

teo_99 2023. 8. 22. 19:14

매번 협업 프로젝트를 진행할 때마다 마주치는 빈번한 문제 중 하나는 CORS입니다. 이번 협업 프로젝트를 진행하면서도 CORS 문제를 다룰 일이 생겼고, 한 번은 제대로 짚고 넘어가면 좋을 것 같아 이번 아티클을 통해 정리해 보도록 하겠습니다.
 

Origin

SOP(Same Origin Policy)와 CORS(Cross Origin Resource Sharing)에 대해 알아보기에 앞서, 두 개념에서 공통적으로 사용되는 개념인 Origin에 대해 알아보도록 하겠습니다. 
 

Web content's origin is defined by the scheme (protocol), hostname (domain), and port of the URL used to access it. Two objects have the same origin only when the scheme, hostname, and port all match. - MDN Web Docs

웹에서의 Origin이란 URL에서 scheme, hostname, port를 합친 부분을 의미합니다. Origin이 같다는 의미는 이 세 가지 부분이 모두 같다는 의미와 동일합니다. 예를 들어, 티스토리 블로그의 Origin은 다음과 같이 구성됩니다.
 

티스토리의 Origin

만약 한 부분이라도 일치하지 않으면 '같은 Origin이다' 라고 이야기할 수 없습니다. 호스트명, 프로토콜이 같다고 하더라도 사용하는 포트번호가 다르다면 같은 Origin이 아닙니다.
 
그리고 이러한 Origin이라는 개념이 SOP, CORS의 기반이 됩니다.
 

SOP(Same Origin Policy)

SOP란 '동일 출처 정책'으로, 동일한 Origin에 대해서만 자원을 공유할 수 있다는 정책을 의미합니다. 그런데 왜 동일한 출처(Origin)에 대해서만 자원을 공유해야 할까요? 
 
웹사이트끼리 자원을 아무런 제약 없이 주고받을 수 있다면 보안 관련 문제가 여럿 생기기 때문입니다. 이와 관련한 대표적인 문제로는 CSRF(Cross Site Request Forgery), XSS(Cross Site Scripting)이 있습니다. 
 

 
만약 SOP가 없다면 네이버에 접속한 유저는 로그인을 하든, 메일을 보내든 이 요청이 '네이버 서버'로 안전하게 전달될 것이라는 확신을 가질 수 없습니다. 웹사이트끼리 자유롭게 소통을 할 수 있는 환경이라면 네이버가 아닌 어떤 악성 사이트로 요청이 보내질 수도 있는 것이죠.
 
추가적으로 아셔야 할 부분은, 이러한 SOP와 같은 출처 정책은 브라우저에서 구현하는 스펙이라는 것입니다. 서버 간의 통신에는 SOP라는 개념이 적용되지 않습니다. 

따라서 브라우저를 거치지 않고 서버에 직접 요청을 보내는 경우 CSRF, XSS 관련 문제가 여전히 발생할 수 있습니다. 그럼에도 브라우저에서 SOP를 기본적으로 지원하는 이유는 '사용자의 민감한 정보'를 다루는 게 브라우저이기 때문입니다. 1차적인 보안 대책이라고 볼 수 있는 것이죠.
 
하지만 모든 웹페이지가 이렇게 SOP를 준수해서 다른 Origin과 통신 하지 않는 것은 불가능한 일입니다. 다른 출처의 리소스를 가져와 사용하는 일은 굉장히 흔한 일이니까요. 그래서 SOP 제약조건을 한정적으로 느슨하게 할 수 있는 방법이 생겨났는데, 그 방법이 바로 CORS(Cross Origin Resource Sharing)입니다.
 
 

CORS(Cross Origin Resource Sharing)

앞서 설명했다시피, 다른 출처에 대해 SOP를 한시적으로 허용해주는 방법이 바로 CORS입니다. 해석하면 '교차 출처 자원 공유'가 됩니다.
 
 
웹 어플리케이션을 개발하다 보면 한 번쯤 맞닥뜨리는 개념입니다. 웹 개발을 한다고 하면 보통 프론트엔드 서버, 백엔드 서버로 나눠서 개발을 진행하는데, 각 진영의 프레임워크(React, Spring 등)가 사용하는 포트번호가 다르므로 Origin이 다릅니다. 따라서 SOP 정책을 위반하게 되는 것이죠.
 
그럼 어떻게 CORS를 잘 활용해 서로 다른 Origin을 가지는 서버끼리 통신을 할 수 있을까요?
 
흐름에 따라 CORS는 크게 3가지 동작방식을 가지는데, 하나씩 알아보도록 하겠습니다.
 

1. Preflight Request

preflight request를 직역하면 '사전 요청'입니다. 대부분의 웹 어플리케이션에서는 CORS 문제를 해결할 때 이 방법을 사용한다고 생각하시면 됩니다. 플로우는 다음과 같습니다.
 

  1. 브라우저에서 본 요청에서 사용할 메소드, 헤더 등을 Access-Control-Request-Method, Access-Control-Request-Headers 헤더에 실어서 OPTIONS 메소드로 사전 요청을 보냅니다.
  2. 서버는 이에 대한 응답으로 허용하는 Origin, Method, Headers와 캐싱이 가능한 시간인 Max-Age 등을 응답으로 보냅니다.
  3. 브라우저는 서버의 응답과 예비 요청을 대조합니다.
  4. 만약 요청이 안전하다고 판단되면 본 요청을 보내고, 그렇지 않다면 CORS 에러를 발생시킵니다.

 
'이 요청이 보내도 되는 요청인지'를 판단하기 위해 예비 요청을 보내는 방식이라고 생각하시면 될 것 같습니다. 하지만 매 요청마다 예비 요청을 보내는 건 네트워크 비용에 부담이 될 수 밖에 없는데, 그래서 서버는 Access-Control-Max-Age 헤더와 함께 응답해서 캐싱 가능한 시간을 지정해 줍니다. 
 

2. Simple Request

예비 요청을 생략하고 바로 본요청을 보내는 방식을 의미합니다.
 

 

  1. 브라우저는 사전 요청 없이 바로 본 요청을 보냅니다.
  2. 서버는 정상 응답을 하면서, 허용된 Origin에 대한 헤더까지 같이 실어서 보냅니다.
  3. 브라우저는 서버 응답과 요청의 Origin을 비교해서 다르다면 CORS 에러를 발생시킵니다.

 
Simple Request 방식은 플로우가 굉장히 간단하지만, 사용하기 위해서는 만족해야 하는 제약조건들이 여럿 있습니다. 
 

  • HTTP METHOD는 명시된 세 가지 중 하나를 사용해야 함
    • GET, HEAD, POST
  • media type은 아래 세 가지 중 하나를 사용해야 함
    • application/x-www-form-urlencoded, multipart/form-data, text/plain
  • 아래의 나열된 헤더 이외에는 직접 지정할 수 없음
    • Accept, Accept-Language, Content-Language, Content-Type, Range

웹 어플리케이션에서 보내는 대부분의 요청은 위 제약조건들을 모두 만족하기 어려우므로, Simple Request는 특정한 상황이 아니라면 자주 사용되지 않는다고 생각하셔도 됩니다.
 

3. Credentialed Request

요청에 인증 정보가 포함되는 경우(Authorization 헤더, 쿠키 등)에 사용되는 방식입니다. 
 

preflight request 방식과 상당히 유사하지만, 자격 증명(credential)에 대한 검증 기능이 하나 더 추가되었다고 생각하시면 됩니다. 서버는 요청에 대해 응답할 때, 자격 증명을 같이 전송해도 되는지에 대한 헤더(Access-Control-Credentials)를 true or false로 보냅니다. 
 
그리고 이 응답을 받은 브라우저에서는 자격 증명이 포함된 본 요청을 보내도 되는지 아닌지를 판단하게 됩니다. 만약 Authorization 헤더를 같이 보내야 하는 요청인데 Access-Control-Credentials가 'false'로 응답되었다면 CORS 에러가 발생하게 됩니다.
 

참고로, Access-Control-Credentials 헤더가 true로 설정되면 Acess-Control-Allow-Origins는 와일드카드(*)로 설정할 수 없습니다. 인증 정보는 민감하기 때문입니다.

 

마치며

Origin의 대한 개념부터 시작해서 SOP 그리고 CORS의 개념 및 종류까지 알아보았습니다.
 
감사합니다.
 

참고 자료

https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F
https://developer.mozilla.org/en-US/docs/Glossary/Origin
https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
https://stackoverflow.com/questions/51795951/same-origin-policy-is-browser-restriction-is-it-secure-on-server-side