티스토리 뷰

시리즈/Web

proxy와 CORS 1편

빅또리 2021. 4. 22. 17:32

CORS가 뭔지 알아보려고 구글링하는데 create-react-app으로 생성한 react랑 express서버를 프록시나 cors설정으로 연결하는 블로그 글이 있었다.

react로 뭘 만들어본 적이 없어서 그 글을 읽어도 어떤 맥락에서 저런 설정을 해야하는건지 낯설었다.

이번에 알아보도록 하자.

 

 

 

 

 

개념 알아보기

proxy

In computer networking , a proxy server is a server application or appliance that acts as an intermediary for requests from clients seeking resources from servers that provide those resources.    - 위키피디아

 

 

forward proxy vs reverse proxy

 

 

참고하기 : https://oxylabs.io/blog/reverse-proxy-vs-forward-proxy

 

 


 

same origin policy

 Under the policy, a web browser permits scripts contained in a first web page to access data in a second web page, but only if both web pages have the same origin. 

기본적으로 보안상의 이유로 웹에서 스크립트 내에서 다른 사이트의 컨텐츠 요청은 같은 오리진일 경우에만 가능하다.

 

 

 

origin (오리진)

프로토콜, 도메인, 포트번호 세가지로 결정됨

 

 

 

 

CORS(Cross-Origin Resource Sharing, 크로스 오리진 리소스 공유)

하지만 웹의 발전에 따라 Cross-Origin Request(크로스 오리진 요청, 다른 오리진으로 요청하는 경우)도 가능해야 할 필요성이 생겼다.

그러던 와중에 브라우저측 자바스크립트에도 네트워크 관련 메서드가 추가됩니다.

처음 네트워크 요청 메서드가 등장했을 때엔 크로스 오리진 요청이 불가능했습니다. 하지만 긴 논의 끝에 크로스 오리진 요청을 허용하기로 결정합니다. 대신 크로스 오리진 요청은 서버에서 명시적으로 크로스 오리진 요청을 ‘허가’ 했다는 것을 알려주는 특별한 헤더를 전송받았을 때만 가능하도록 제약을 걸게 됩니다.
                                                                                             -ko.javascript.info/fetch-crossorigin

이제는 오리진이 다르더라도 다른 사이트가 해당 오리진의 크로스 오리진 요청을 허용 하는 경우에는 보낼 수 있다.

( 브라우저를 중개자로 삼아 헤더의 특별한 필드에 담긴 정보로 가능한 크로스 오리진 요청인지 판단하게 된다. )

 

이런식으로 작동한다.

 

 

 

CORS 요청을 크게 2가지 컨셉에 따라 구분해 볼 수 있다.

1. preflight 요청이 필요한가 (안전한 요청 vs 안전하지 않은 요청)

2. credentials (자격증명)을 함께 동봉해서 보내는 요청인가

 

1.

이런 안전한 요청은 위 그림 같이 한번만 왕복하면 되는데

그 외의 다른 모든 크로스 오리진 요청은 이렇게 해야한다고 한다.

 

2.

자바스크립트로 크로스 오리진 요청을 보내는 경우, 기본적으로 쿠키나 HTTP 인증 같은 자격 증명(credential)이 함께 전송되지 않습니다.
이런 예외가 생긴 이유는 자격 증명과 함께 전송되는 요청의 경우 영향력이 강하기 때문입니다. 크로스 오리진 요청 시 자격 증명을 함께 전송할 수 있으면 사용자 동의 없이 자바스크립트로 민감한 정보에 접근할 수 있게 됩니다.
그럼에도 불구하고 서버에서 이를 허용하고 싶다면, 자격 증명이 담긴 헤더를 명시적으로 허용하겠다는 세팅을 서버에 해줘야 합니다.
자격 증명 정보가 담긴 요청을 서버에서 받아들이기로 동의했다면 서버는 응답에 Access-Control-Allow-Credentials: true 라고 해주기
                                                                               -https://ko.javascript.info/fetch-crossorigin

 

CORS에 대한 다른 글

CS Visualized: CORS

fetch와 Cross-Origin 요청

Cross Origin Resource Sharing - CORS

 

 

 

 

+ Window.postMessage()

The window.postMessage() method safely enables cross-origin communication between Window objects
e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.

메세지 보내기
메세지 받기

 

 

 

 

보편적인 상황 시뮬레이션

 

생각할 점

same origin polcy는 사이트의 스크립트 태그 내부에서의 요청을 규제하는 것이다. (다른 오리진으로 요청을 보내지 못하도록)

1. 즉, 노드에서 날리는 http request (예를 들면 axios나 request라이브러리 등을 사용해서)는 전혀 규제 대상이 아니다.

그러니 서버에서 다른 오리진으로 요청보내는 건 만사 오케이.

 

2. 또, 클라이언트에서의 요청이라 해도, <img>, <link>, <video> ,<audio>, <object>, <embed> ,<applet>

등의 태그로 다른 오리진에서 이미지, css 등등을 요청하는 경우도 SOP의 대상이 아니라고 한다.

 

3. 하지만 스크립트 태그 내부의 자바스크립트로 XMLHttpRequest, ajax, Fetch API를 이용해 http request를 보내는 경우에는 SOP를 지켜줘야한다. 

 

위의 사실을 고려해서 이런식의 실험( ? 이라고 하긴 거창한데 )환경을 구성해봤다.

 

 

 

 

 

 

서버A

서버 B, 클라이언트 B

클라이언트 B에서 get 요청 url /students

실행결과

딱히 CORS 관련 허용해주는 설정 한 것 없는데 잘 동작된다.

서버C, 클라이언트 C

클라이언트 C에서 get 요청 url http://localhost:8000/students

실행결과

CORS policy 에러 메세지만 나오고 안된다.

 

 

 

 

 

 

그럼 클라이언트의 스크립트에서 CORS policy를 위반하지 않고 어떻게 http request를 잘 보낼 수 있을까?

해결방법 

[1] proxy 서버 두기 ( => B의 케이스 처럼 하기 )

[2] 응답을 줄 서버측에 CORS 허용 하는 설정 해두기 ( => C처럼 직접 보내는 요청이지만 A가 ok 해주는 케이스 )

 

 

(1) 클라이언트D와 같은 오리진인 서버D프록시 서버로 활용하기

http-proxy-middleware 모듈의 createProxyMiddleware 함수를 사용해서 구현했다.

 

서버D , 클라이언트 D

서버 D

(클라이언트 코드는 B랑 동일)

 

실행결과

성공적

 

(2) 서버 A에서 클라이언트E의 오리진에서 오는 크로스 오리진요청은 허용한다는 설정해두기 (CORS config)

 cors 모듈 활용해서 구현

 

서버E, 클라이언트 E => C케이스와 동일

서버 A를 다음과 같이 바꿔준다

 

실행결과

response header의 Access-Control-Allow-Origin을 주목해보라

cors 모듈 사용할때 그냥 간단히 app.use(cors()); 이런식으로 사용하면 모든 오리진을 허용한다.

Access-Control-Allow-Origin : *

 

 

 

코드 보기 : github.com/jjyoummmin/proxy_cors/tree/main

 

 

 

+다른 블로그 보니까 꼼수 같은 느낌으로? jsonp로 요청을 보내거나 jquery.ajaxPrefilter()를 사용하는 방법도 있다는 것 같다.

댓글
공지사항
최근에 올라온 글