[Web] CORS에러와 SOP정책

coderH·2022년 10월 8일
0
post-thumbnail

CORS에러와 SOP정책

이번 글에서는 웹에서 자주 접하는 개념이자 프론트엔드 면접 단골질문으로 뽑히는 CORS에 대해서 알아보려고 합니다.

CORS에러는 브라우저의 보안 정책 중 하나인 SOP정책에 의해 발생하기 때문에 먼저 SOP가 무엇인지 알아보겠습니다.

SOP란?

SOP(Same-Origin Policy, 동일 출처 정책)는 브라우저의 보안 정책 중 하나로 오직 동일한 출처끼리만 데이터를 주고 받을 수 있도록 하는 정책을 말합니다.

예를 들어, 우리가 www.test.com 라는 사이트에 접속을 했고 여기에서 로그인을 위해 www.server.net 으로 요청한다면 이는 서로 다른 출처이기 때문에 브라우저의 SOP정책으로 인해 정상적으로 응답이 도착하더라도 브라우저가 데이터를 읽을 수 없도록 null로 처리해버리게 됩니다.

그럼 여기서 말하는 동일 출처의 기준은 무엇일까요?

SOP정책에서 말하는 동일 출처란 URL의 프로토콜, 도메인(서브도메인 포함), 포트 이 3가지 요소가 모두 동일할때만 동일한 출처라고 판단합니다.

우리가 접속한 사이트가 https://www.naver.com:443이라고 가정해보고 몇 개의 URL로 요청을 보내보겠습니다.

URL응답결과
http://www.naver.com❌ 프로토콜이 달라 실패 (http vs https)
https://www.naver.com:8080❌ 포트번호가 다르므로 실패 (443 vs 8080)
https://blog.naver.com:443❌ 서브도메인이 다르므로 실패
https://www.google.com❌ 도메인 자체가 다르므로 실패
https://www.naver.com:443/search✅ 프로토콜, 도메인, 포트번호가 같으므로 성공

여기서 궁금한점이 그럼 왜 SOP 정책을 만들었을까요?

과거의 웹은 사이트간 데이터를 요청하는 일이 거의 없었습니다.

따라서 서로 다른 출처에서 데이터를 주고 받을 수 있다는 것은 XSS, CSRF와 같은 스크립트를 이용한 공격으로
사용자의 데이터가 쉽게 유출될 수 있기 때문에 이러한 공격을 방지하기 위해 SOP 정책을 도입하게 되었습니다.

하지만 점점 웹이 발전함에 따라 서로 다른 사이트끼리도 데이터를 주고 받을 일이 생기게 되었고 당시 개발자들은 SOP정책으로 인해 데이터를 받을 수 없자 JSONP라는 패턴을 사용한 요청을 보내 브라우저의 보안 정책을 우회하여 데이터를 주고 받게 됩니다.

  • JSONP(JSON with Padding) 란?

우리가 사이트에 접속해서 보내는 요청은 주로 Fetch API나 XHR을 이용하며 이들은 SOP정책이 적용됩니다.

하지만 img, iframe, video같은 태그들은 SOP정책이 적용되지 않는데 특히 script 태그에서도 적용되지 않는다는 점을 이용해 보안 정책을 우회하는 기법으로 CORS가 생기기 전에 사용되던 방법입니다.

JSONP 패턴은 클라이언트와 서버에서 사전에 콜백함수의 이름을 미리 정해두고

클라이언트에서 script의 src속성값으로 사용되는 URL의 쿼리 부분에
URL?callback=callbackFn() 형식으로 콜백함수를 뒤에 붙여서 script태그를 작성하게 됩니다.

요청을 받은 서버에서는 이 콜백함수의 인자로 사용자의 데이터를 담아 클라이언트에게 응답을 보내고 script태그는 응답을 받으면 바로 코드가 실행되므로 해당 콜백함수가 실행되며 인자로 받은 데이터를 사용합니다.

callbackFn(data) {
  console.log(data);
}


<script src="https://www.server.net?callback=callbackFn()"></script>

다만, JSONP는 GET 요청으로만 사용이 가능하며 에러 핸들링이 까다롭고
만약 서버가 공격당했다면 클라이언트에 XSS 공격이 가능한 점 등 여러 문제점이 있었기 때문에

W3C에서는 이러한 보안 이슈를 막기 위해 서로 다른 출처에서도 리소스를 공유할 수 있도록 하는 CORS를 만들게 됩니다.

CORS란?

CORS(Cross-Origin Resource Sharing, 교차 출처 리소스 공유)는 이름 그대로 서로 다른 출처임에도 데이터를 주고 받을 수 있도록 한것입니다.

즉, 기존에는 "절대 불가"였다면 CORS를 만든 후에는 "서버에서 허락했으면 가능" 으로 변경된 것입니다.

따라서 CORS에러가 발생한다는 것은 "SOP정책때문에 서버에서 허락해야 리소스 공유 할 수 있으니까 CORS 설정해" 라는 뜻이 담겨있는 에러인 것입니다.

자 그럼 CORS 에러를 어떻게 해결해야 할까요?

CORS 해결 방법

서버에서 해결하기

CORS를 해결하는 방법은 가장 기본적으로 서버에서 설정 해주는 것입니다.

우리가 스크립트로 보내는 모든 요청의 헤더에는 origin이 기본적으로 포함되어 있습니다.
이는 서버에게 해당 요청이 어디에서 왔는지 알려주는 역할입니다.

또한 서버에서 보내는 응답에는 Access-Control-Allow-Origin이라는 헤더가 있습니다.
이 헤더에 서버에서 허용한 사이트의 URL을 명시해 브라우저에게 "아 얘네는 우리가 허락한 출처야" 라고 알려주는 역할을 합니다.

그래서 브라우저는 클라이언트 요청 헤더의 origin값과 응답 헤더의 Access-Control-Allow-Origin값을 비교해 같다면 "아 서버에서 허락한 출처니까 데이터를 읽을 권한이 있구나"라고 판단해 데이터를 정상적으로 보여주게 됩니다.

그래서 우리가 API를 이용하다 보면 종종 서비스되는 웹의 도메인을 입력하라고 하는 이유가 우리가 개발한 사이트의 URL을 이 응답 헤더의 값으로 넣기 위함입니다.

물론, 와일드카드인 *기호를 이용해 모든 사이트를 허락할 수도 있습니다.

다만, 요청 헤더에 Credentials와 같은 민감한 인증 정보가 포함된 요청의 경우 와일드카드는 무시되기 때문에 상황에 맞게 사용해야 합니다.

클라이언트에서 해결하기

하지만 만약 서버의 응답 헤더 값을 수정할 수 없는 상황이라면 어떻게 해야할까요?

클라이언트에서 해결할 수 있는 방법이 있습니다. 프록시 서버를 이용하는 방법입니다.

위에서 SOP는 브라우저의 정책이라고 말씀드렸습니다.
즉, 서버에서 별도의 로직을 작성하지 않으면 기본적으로 모든 요청에 응답하며 사용자가 사용하는 브라우저가 SOP정책에 따라 해당 데이터를 읽지 못하도록 처리 할 뿐입니다.

이 말은 SOP정책은 클라이언트와 서버간의 요청과 응답에만 해당된다는 말입니다.

프록시 서버를 이용 할 경우 서버와 서버간의 요청과 응답이기 때문에 SOP정책이 적용되지 않아 클라이언트는 프록시서버에게, 프록시서버는 리소스서버에게 요청함으로써 CORS에러를 해결할 수 있습니다.

Postman이라는 플랫폼을 사용해보신 분이라면 Postman에서는 CORS에러가 발생하지 않는다는것을 알 수 있는데 그 이유가 바로 Postman의 서버에서 보내는 요청이기 때문입니다.

아래 링크는 CORS를 세팅해 볼 수 있는 CORS playground라는 사이트로 CORS를 직접 테스트 해보기 좋습니다.

CORS playground

출처

교차 출처 리소스 공유 (CORS) | MDN

sskey | Tistory

hoit_98 | Velog

0개의 댓글