CORS의 개념 / 존재 이유 / 원리

방충림·2023년 3월 23일
4

CS

목록 보기
19/26
post-thumbnail

한 사이트에서 주소가 다른 서버로 요청을 보낼 때, 자주 접하게 되는 오류가 있다. 다음와 같은 CORS관련 오류이다.

예를들어 API를 통해 어떤 정보를 받아오기 위해 '프론트에서' HTTP요청을 보냈을 때 미리 어떤 설정을 해 주지 않으면 CORS 문제로 막히게 된다.

이를 PostMan이나 백엔드에서 HTTP로 요청을 보내보면 그곳에선 다 되는데 프론트에서만 Rest API로 보내면 정상적으로 되지 않는 것을 볼 수 있다.

웹사이트에서 AJAX를 보냈을 때에만 안 된다는 것은, 브라우저라는 곳에서 해당 오류가 일어난다는 이야기다.

이런 문제가 발생한다면 왠지 api쪽에서 막을 것 같지만 사실 CORS라는 이유로 요청을 막는 것은 이 브라우저 쪽이다. 내 브라우저가 그 사이트를 못 믿는 것이다.

CORS가 존재하는 이유

예를들어 A라는 접근하고자하는 정상적인 사이트가 있고, B라는 악의적인 사이트가 있다.
사용자는 A에 로그인해 있고 그 상태가 유지되고 있다.
그것은 인증정보가 사용자의 브라우저에 쿠키로 저장되어 있다는 것을 의미한다.

그런데 A로부터 사용자의 정보를 빼내기 위해 해커들이 B라는 악의적인 사이트를 만들었다. 그리고 사용자가 페이지에 접속하도록 유도한다. 해당 페이지에 사용자가 접속을하면 B가 만든 HTML,CSS,JS코드가 사용자의 브라우저에 받아와진다는 것이고 그 코드로 나에 브라우저한테 무슨 짓을 시킬지 모르는 것(XSS?)이다. 그리고 그곳엔 인증정보까지 있다.

XSS (Cross Site Script)
검증되지 않은 외부 입력 데이터가 포함된 웹페이지가 전송되는 경우, 사용자가 해당 웹페이지를 열람함으로써 웹페이지에 포함된 부적절한 스크립트가 실행되는 공격

A로부터 개인정보를 조회하는 요청에 사용자의 브라우저에 저장된 A페이지의 토큰을 실어서 A에 보낸다음 그것으로 탈취한 정보를 B 서버로 보내버릴 수 있다. 즉 사용자의 의지와 상관없이 거기에 저장된 내 정보들을 가지고 악의적인 행위(CSRF?)을 할 수가 있기 때문에 브라우저가 이런 것을을 방지하고 있었던 것이다.

CSRF (Cross-Site Request Forgery)
사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하게 하는 공격

정확히 말하자면 CORS는 그걸 막는게 아니라 오히려 풀어주는 역할을 한다. 요청을 막고 있는 건 SOP(Same-Origin Policy, 동일 출처 정책)가 하는 것이다.
SOP이라는 것은 말 그대로 동일한 출처, 똑같은 URL끼리만 API등의 데이터에 접근이 가능하도록 막는 막는 것이다.
CORS라는 것은 "이게 되게하려면 CORS라는 것을 허용해주던가 해라"라는 의미인 것이다.

CORS 개념

CORS는 Cross Origin Resource Sharing 의 약자로, 동일 출처(Same-Origin)의 반대 개념이다. 말 그대로 다른 출처간에 리소스를 공유할 수 있도록 하는 것이다.

출처 = 보내고 받는 각각의 위치(즉 웹사이트랑 API의 주소)
리소스 = 주고받아지는 데이터

이 다른 출처끼리 정보요청과 반환이 가능하도록 하는게 CORS라는 것이다.

사실 서로 다른 출처끼리 요청을 주고 받는 건 안 되는 게 기본값이다. 브라우저에서는 애초부터 금지되어 있었다.

하지만 웹 생태계가 다양해지면서 여러 서비스들 간에 보다 자유롭게 데이터를 주고 받아질 필요가 생겼고, 그것을 되게 하기위해 과거 개발자들은 JSONP(요즘은 잘 안씀)등의 방식으로 이를 우회하는 꼼수들을 사용하기 시작했다.

그래서 이걸 합의된 출처들간에 합법적으로 허용해주기 위해 어떠한 기준을 충족시키면 리소스 공유가 되도록 만들어진 메커니즘이 바로 이 CORS, 교차 출처 자원 공유 방식이다.

그 조건이라는 것은 간단하다. 요청을 받는 백엔드쪽에서 이걸 허락할 다른 출처들을 미리 명시해두면 되는 것이다.

백엔드 서버 프레임워크(스프링, 장고, Express 등)의 문서를 살펴보면 CORS옵션을 넣는 방법들이 쉽게 마련되어 있다.

여기에 허용할 사이트들을 적어주면 거기서 지정한 사이트에서는 이 서버로 얼마든지 HTTP요청을 보낼 수 있는 것이다.

아무나 보내도 되는 요청의 경우, 일반적인 방법으로 별표,와일드카드를 적어넣으면 누구나 쓸 수 있게 된다.

조금 더 자세히 탐구해보자

원리

브라우저는 다른 출처끼리 요청이 보내질 때는 요청에 Origin이라는 Header(데이터가 다른 곳으로 전송될 때 데이터의 맨 앞쪽에 붙은 보충 정보, 받는 쪽의 IP주소, 사용할 프로토콜이나 옵션 등이 담김, 우편에 비유하면 봉투에 적힌 내용)를 추가한다.

이 Header의 Origin 항목에는 요청한 scheme(요청할 자료에 접근할 방법 <http, ftp, telnet> 등)과 도메인, 그리고 포트가 담긴다.

이 요청을 받은 API서버는 답장의 헤더에 지정된 Aceess-Control-Allow-Origin정보(등록된 URL정보)를 실어서 보낸다.

그러면 내 브라우저가 Origin에서 보낸 출처값이 서버의 답장 헤더에 담긴 서버의 답장 헤더에 담긴 Aceess-Control-Allow-Origin에 똑같이 있으면 안전한 요청으로 간주하고, 응답 데이터를 받아오게 되는 것이다.

민감정보가 담긴 요청의 경우

토큰 등 사용자 식별 정보가 담긴 요청에 대해서는 보다 엄격하다.
일단 보내는 측에서는 요청의 옵션에 credentials항목을 true로 세팅해야하고, 받는 쪽에서도 아무 출처나 다 된다는 와일드카드가 아니라 보내는 쪽의 출처 - 웹페이지 주소를 정확히 명시한 다음 Aceess-Control-Allow-Crediential항목을 true로 맞춰줘야한다.

브라우저에 저장된 쿠키가 나쁘게 쓰일 수 있는 것이기 때문에 양쪽이 보다 엄격하게 조건을 갖춰놓아야 하는 것이다.

이 방식은 Simple request라고해서 GET이나 POST등 일정 조건의 요청들에서 사용되는 것이고, PUT이나 DELETE등 이와 다른 요청들은 본 요청을 보내기 전에 Preflight요청이란 것을 먼저 보내서 본 요청이 안전한지 확인하고, 여기서 허락이 떨어져야 본격적으로 요청을 보낼 수가 있다. 서버의 데이터에 영향을 줄 수 있는 요청들이기 때문에 요청 자체를 보내기 전에 먼저 허용 여부를 검증하는 것이다.

정리하자면 CORS에서 요청들은 두 종류, Simple request와 Preflighted 요청으로 나뉘는데 전자는 요청을 보내기는 다 보내는데 통과를 못하면 답장만 못 받아오는 것이고, Preflighted 요청은 요청을 '보내는'것도 일단 허락을 받아야한다.

물론 Simple request도 받는 쪽에서 신경쓰지않으면 서버에 저장된 데이터에 변경이 가해질 수도 있다. 그래서 SOP만 믿을게 아니라 개발자 측에서도 그런 것을에 대비해서 서버를 프로그래밍해야하는 것이다.



참고문헌 : 얄팍한 코딩사전

profile
최선이 반복되면 최고가 된다.

0개의 댓글