[Web] CORS

한호성·2022년 8월 14일
0

글의목적


CORS(Cross-Origin Resource Sahring)의 약자이다. 요번주에 Front(react)와 함께 협업을 진행하게 되면서,Front 에서 내가 만든 Spring api에 요청을 보낼 때, cors 오류가 날 수 있다고 한다. 정확히 CORS가 무엇인지 알아보자

CORS


기본개념

CORS ( Cross-Origin Resource Sharing) 의 줄임말로, "교차 출처 리소스 공유"정도로 해석할 수 있다. 서로 다른 출처간의 자원을 공유하는 것에 대한 정책을 의미한다.

이것에 대해 자세히 알기 위해서 URL을 뜯어보자

위의 그림에서 CORS의 Origin은 Protocol 과 Host(port 번호 포함) 까지를 의미한다.

실제로 개발자 도구를 통해서 내가 작성하고 있는 벨로그의 Origin을 알아보았다. 위 사진과 같이
"https://velog.io"가 origin인 것을 파악할 수 있다.

CORS란 것은 출처가 서로 다른 두 개의 어플리케이션이 마음대로 소통할 수 있는것에 제한을 두는 정책이다. 이런 것들이 꼭 필요한가? 에 대해 생각해보자.

우리가 CHROME의 개발자 도구를 통해서, NETWORK탭에서 굉장히 많은 정보를 알 수 있고,CORS정책이 없다면, 악의를 가진 사람들이 소스코드를 통해 CSRF ,XSS같은 방법을 사용하여, 사용자의 정보 탈취가 쉬워진다고 한다.

#cf) *CSRF,XSS가 무엇인지 찾아보자..

출처(Origin)비교

같은 출처라고 파악되는 것은 위의 그림에서 Protocol,host,port가 같다면 같은 출처로 인정한다는 의미이다.

출처를 비교하는 로직이 서버에 구현된 스펙이아니라 브라우저에서 구현된다고 한다. 만약 우리가 CORS 정책을 위반하는 리소스 요청을 하더라도, 해당 서버가 같은 출처에서 보낸 요청만 받겠다는 로직을 가지고 있는 경우가 아니라면 서버는 정상적으로 응답을 하고, 이후 브라우저가 이 응답을 분석해서 CORS정책 위반이라고 판단되면 그 응답을 사용하지 않고 버린다.

CORS의 동작

웹 클라이언트 어플리케이션이 다른 출처의 리소스를 요청할 때, HTTP 프로토콜을 사용하여 요청을 보내게 된다. 이 때, 브라우저는 요청 헤더에 Origin이라는 필드에 요청을 보내는 출처를 함께 보낸다.

이후 서버가 요청에 대한 응답을 할 때, 응답 헤더의 Access-Control-Allow-Origin이라는 값에 리소스를 접근하는 것이 허용된 출처를 내려준다. 브라우저는 자신이 보냈던 요청의 origin과 서버가 보내준 응답의 Access-Control-ALllow-origin을 비교해 응답이 유효한 응답인지 아닌지 결정한다.

위와 같은 동작은 가장 기본적인 흐름이고, 실제로 CORS는 세 가지의 시나리오에 따라 변화한다고 한다. 잘 알아보도록 하자

Preflight Request

우리의 요청을 한 번에 보내지 않고 예비요청과 본 요청을 나누어서 서버로 전송한다.
이 때, 브라우저가 본 요청을 보내기 전에 보내는 예비 요청을 preflight라 부르며, http 메소드의 OPTIONS 메소드가 이용된다.

간단한 FLOW CHART는 다음과 같다.

Fetch API를 사용하여 브라우저에게 리소스를 받아오라는 명령을 내리면, 브라우저는 서버에게 예비 요청을 보내고, 서버는 예비 요청에 자신이 허용한 정보들에 대해 보내준다.

이후, 브라우저는 예비요청과 서버 응답에 담아준 정책을 비교해, 안전하다고 판단하면 본 요청을 서버에 보내고 응답을 받아온다.

Simple Request

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

단순 요청은 특정 조건을 만족하는 경우에만 가능하다고 한다.

1. 요청의 메소드는 GET,HEAD,POST 중 하나여야 한다.

2  Accept, Accept-Language , Content-Language, Content-type DPR, Downlink, 
Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안된다.

3. 만약 Content-Type를 사용하는 경우에는 application/x-www-form-urlencoded, 
multipart/form-data, text/plain만 허용된다.

2,3번의 조건을 보면, 내가 만든 스프링 서버에서는 단순요청의 조건에 해당하지 않는듯 하다.

Credentialed Request

인증된 요청을 사용하는 방법이다. 이 시나리오는 CORS의 기본적인 방식이라기 보다, 다른 출처간 통신에서 좀 더 보안을 강화하고 싶을 때 사용한다고 한다.

기본적으로 XMLHTTPREQUEST 객체 ,Fetch api는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다고 한다. 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 Credentails 옵션이다.

이 same-origin이나 include와 같은 옵션을 사용하여 리소스 요청에 인증 정보가 포함된다면, 이제 브라우저는 다른 출처의 리소스를 요청할 때 단순히 Access-Control-Allow-Origin만 확인하는 것이 아니라 좀 더 빡빡한 검사 조건을 추가하게 된다.

CORS 해결하기

서버에서 정석대로 Access-Control-Allow-Origin 헤더에 알맞은 값을 세팅해주는 것.
만약 *을 세팅하면, 모든 출처에서 오는 요청을 받아들이겠다는 의미이므로, 이상한 출처에서 오는 요청까지 받으므로, 보안 이슈에 노출될 수 있다.

명시적으로 표시해주는 것이 좋다. 이 헤더는 Nginx or Apache 같은 서버 엔진의 설정에서도 추가할 수 있고, middleware에서 세팅하는 방법도 있다.

#cf) 추가적으로 개발을 진행하면서, CORS 관련해서 어떤것이 문제되는지 Frontend 팀원과 이야기 나누고 어떻게 해결할지에 대해 고민해봐야겠다.

Reference

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

profile
개발자 지망생입니다.

0개의 댓글