SOP와 CORS

MINJU·2022년 8월 27일
0

네트워크

목록 보기
12/12

참조 블로그-1
참조 블로그-2
참조 블로그-3


CORS는 Cross-Origin Resource Sharing의 약자입니다.

간단히 말하면 현재 IP가 아닌 다른 IP로 리소스를 요청하는 구조 라고 생각하면 됩니다.

CORS를 이해하려면 SOP 를 알아야합니다. CORS에러는 SOP라는 "브라우저 원칙"으로 인해 발생하기 때문입니다. SOP는 Same Origin Policy를 말합니다. 이는 2011년 RFC 6454에서 등장한 "보안 정책"으로서 동일한 출처의 Origin만 리소스를 공유할 수 있다는 정책입니다.

여기서 Origin이란 요청이 시작된 서버의 위치를 의미합니다.

scheme, host, port 세 가지가 모두 같아야 same origin으로 봅니다.
same origin인지 출처를 비교하는 로직은 서버가 아닌 "브라우저"에 구현되어 있습니다.



그렇다면 SOP 가 필요한 이유는 무엇일까요?

참조 블로그-4

사용자가 웹사이트에 접근할 때에는 "브라우저의 쿠키"에 "로그인 세션 토큰"을 남기게 됩니다. 해당 토큰으로 인해 사용자는 매번 로그인 할 필요 없이 서비스를 이용할 수 있게 됩니다. 웹 사이트는 토큰을 받아 "파싱, 해석"하여 로그인 되었음을 인지합니다.

그러나 로그인 토큰이 남아있는 사용자 브라우저에서 사용자가 악성 사이트에 접속한 경우, 악성 사이트는 사용자 브라우저 토큰에 접근할 수 있게 됩니다. 악성사이트는 이렇게 얻은 토큰으로 네이버와 같은 웹사이트에 로그인을 요청할 수 있게 되고 이로 인해 악의적인 상황에 놓일 수 있게 됩니다.

"브라우저"는 SOP에 따라 이러한 상황을 막습니다. 브라우저가 해석한 document 내에서 외부리소스들과 상호작용할 때 리소스의 origin이 document의 origin과 다를 때 제한을 두겠다는 것이 SOP이기 때문입니다.

참조 동영상
참조 블로그


SOP는 빈번하게 일어나는 보안적인 문제를 해결할 수 있도록 해주지만, 외부 라이브러리도 사용해야하는 개발자의 입장에선 요청과 리소스를 매번 동일한 출처로만 받을 수는 없습니다. 서버는 점차 확장될 것이고 이로 인해 때로는 다른 회사의 서버를 이용해야(API 사용 등)하는 상황이 자주 발생하기 때문입니다. 따라서 이때 필요한 것이 바로 CORS 인 것 입니다.


MOZILLA에선 CORS를

추가 HTTP 헤더를 사용하여, 한 origin에서 실행 중인 웹 애플리케이션이 다른 origin의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제

라고 설명합니다.


동작방식은 다음과 같은데

  1. 다른출처의 리소스를 요청할 때 브라우저는 요청 헤더의 origin이라는 필드에 "요청을 보내는 출처"를 함께 담아 보냅니다.
  2. 서버가 이 요청에 대한 응답을 할 때 응답 헤더의 Access-Control-Allow-Origin이라는 값에 "이 리소스를 접근하는 것이 허용된 출처"를 내려주고 이후 응답을 받은 브라우저는 자신이 보냈던 요청의 origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 이 응답이 유효한 응답인지 아닌지를 결정합니다.

기본적인 흐름은 이와 같지만, CORS 접근 제어에는 세 가지의 시나리오가 사용됩니다. 우리의 요청이 어떤 시나리오에 해당하는지 잘 파악한다면 CORS 정책 위반으로 인한 에러를 고치는 것이 한결 쉬워질 것입니다.

참조 블로그

Preflight Request

가장 빈번하게 마쥐는 시나리오입니다. 이 상황에서 브라우저는 요청을 한 번에 보내는 것이 아니라 "예비 요청"과 "본 요청"으로 나눠서 서버로 전송합니다.

이때 "예비 요청"을 "Preflight"라고 부르며, 이 예비 요청에서는 HTTP 메소드 중 OPTIONS 메소드가 사용됩니다. 예비 요청의 역할은 본 요청을 보내기 전, 브라우저 스스로 이 요청이 안전한지 확인하는 것입니다.

브라우저는 서버에게 예비 요청을 보내고, 서버는 이 예비 요청에 대한 응답으로 자신이 어떤 것을 허용하고, 금지하고 있는지 에 대한 정보를 응답 헤더에 담아서 브라우저에게 다시 보내주게 됩니다.

이 후 브라우저는 "예비 요청"과 서버가 응답에 담아준 "허용 정책"을 비교한 후, 이 요청을 보내는 것이 안전하다고 판단되면 같은 엔드 포인트로 다시 "본 요청"을 보내게 됩니다. 이후 서버가 이에 대한 응답을 하면 브라우저는 최종적으로 이 응답 데이터를 자바 스크립트에게 넘겨주는 것입니다.

상단 참조 블로그의 예시와 동일하게, 참조 tistory 블로그의 RSS 파일 리소스에 요청을 보내보도록 하겠습니다.

const headers = new Headers({
  'Content-Type': 'text/xml',
});
fetch('https://evanmoon.tistory.com/rss', { headers });

이에 대한 요청은 다음과 같습니다.


설명한 것과 동일하게 HTTP 메소드 OPTIONS 를 사용하고 있음을 확인할 수 있습니다. 또한 요청을 보낸 Origin과 "본 요청에 대한 정보"들도 함께 포함되어 있음을 확인할 수 있습니다.

이에 대한 응답은 다음과 같습니다.

Access-Control-Allow-Origin 헤더에 담겨진 정보로 보아 티스토리 서버는 이 리소스에 접근 가능한 출처는 오직 https://evanmoon.tistory.com 뿐이라고 브라우저에게 알려주고 있음을 확인할 수 있습니다. 이로 인해 브라우저는 이 요청이 CORS 정책을 위반했다고 판단하고 에러를 뱉게 되는 것입니다.

Simple Request

이 요청은 예비 요청을 보내지 않고 바로 서버에게 본 요청부터 보내고, 서버가 이에 대한 응답 헤더에 Access-Control-Allow-Origin과 같은 값을 보내주면 그때 브라우저가 CORS 정책 위반 여부를 검사하는 방식입니다.

아무때나 단순 요청을 사용할 수 있는 것은 아니고 특정 조건이 충족되어야만 예비 요청을 생략할 수 있다고 합니다. 하지만 이 조건이 조금 까다로워 ?? 일반적으로 마주하기 쉽지 않아 빈번하게 마주하는 시나리오는 아니라고 합니다.

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

Credentialed Request

인증 된 요청을 사용하는 방법입니다. 다른 출처간 통신에서 보안을 좀 더 강화하고 싶을 때 사용하는 방법입니다.

기본적으로 브라우저가 제공하는 XMLHttpRequestfetch는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않습니다. 이때 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 credentials 옵션입니다.

이 옵션에는 세 가지의 값을 사용할 수 있으며 다음과 같습니다.

  • (same-origin) = 같은 출처간 요청에만 인증 정보를 담을 수 있다.(default)
  • (include) = 모든 요청에 인증 정보 담을 수 있다.
  • (omit) = 모든 요청에 인증 정보 담지 않는다.

omit이 아닌 다른 옵션을 사용한다면, 브라우저는 다른 출처의 리소스를 요청할 때 'Access-Control-Allow-Origin'만 확인하는 것이 아니라 좀 더 철저한 검사 조건을 추가하게 됩니다. 즉 요청에 인증 정보가 담겨있는 상태에서 다른 출처의 리소서를 요청하게 되면 브라우저는 CORS 정책 위반 여부를 검사하는 룰에 다음 두 가지를 추가하게 되는 것입니다.

  1. Access-Control-Allow-Origin에는 *을 사용할 수 없으며 명시적인 URL 이어야한다.
  2. 응답 헤더에는 반드시 Access-Control-Allow-Credentials : true가 존재해야 한다.

그렇다면 이러한 시나리오와 같이 CORS 정책이 위반된 상황에선 어떻게 CORS 정책 위반 문제를 해결할 수 있을까요?

가장 대표적인 방법은 정석대로 서버에서 Access-Control-Allow-Origin 헤더에 알맞은 값을 세팅해주는 것 입니다.

이 헤더는 Nginx나 Apache와 같은 서버 엔진의 설정에서 추가할 수도 있지만, Spring, Django와 같은 백엔드 프레임워크의 경우에는 모두 CORS 관련 설정을 위한 세팅이나 미들웨어 라이브러리를 제공하고 있으니 세팅 자체가 어렵지는 않을 것입니다.


CORS 정책은 브라우저의 구현 스펙이어서 정책 위반으로 인해 문제를 겪는 사람은 대부분 프론트엔드 개발자지만, 문제를 해결하기 위해서는 백엔드 개발자가 서버 응답 헤더에 올바른 Access-Control-Allow-Origin이 내려올 수 있도록 세팅해줘야하기 때문에 조금 복잡합니다.

하지만 CORS를 해결하는 방법 자체가 그렇게 어려운 편은 아니어서 FE나 BE 중 아무나 이 정책에 대해 잘 알고 있으면 빠르고 수월하게 문제를 해결할 수 있습니다.

0개의 댓글