CORS와 Proxy를 활용하여 해결하기

개발 log·2021년 11월 1일
10

JS 지식

목록 보기
24/36
post-thumbnail

아마 프론트엔드 개발자라면 한번쯤은 위의 에러를 본적이 있을 것이다.

이는 CORS에러라고 하는 것인데 본격적으로 CORS를 알기 전에 사전지식으로 SOP란 무엇인지 알아보도록 하겠다.

SOP란?

Same Origin Policy
다른 출처의 리소스를 사용하는 것에 제한하는 보안 방식

Origin(출처)이란?

URL의 Protocol, Host, Port를 통해 같은 출처인지 다른 출처인지 판단할 수 있다.

즉, 셋 중에 하나라도 문자열이 다르다면 다른 출처라고 판단하는 것이다.

단, IE는 Port가 달라도 같은 출처라고 함

왜 SOP를 사용하면 보안에 도움이 될까?

만약 선량한 사용자가 특정 서비스에 로그인을 하면 인증 토큰을 받게 된다.

그 후 만약 해커가 사용자에게 누를 수 밖에 없는 링크를 보내서 만약 사용자가 해당 링크를 클릭하게 된다면 해커가 만든 주소로 이동을 하는데 해커가 만약 해당 페이지에 사용자가 사용하는 특정 서비스에 나는 바보다라는 게시글을 등록하게 하는 스크립트가 작성되어 있는 상태라면 해당 스크립트가 실행되게 되는데 현재 사용자는 특정 서비스의 인증 토큰을 갖고 있는 상태이다.
그렇다면 해커는 그 인증 토큰으로 서비스에 접근하여 나는 바보다라는 게시글을 등록하라고 명령을 하게 된다.
여기서 SOP가 위력을 발휘하는데 특정 서비스에서 Origin을 확인한다.
이 요청이 어디서 온건지 확인하고 다른 출처에서 왔다(COR)고 판단하면 SOP에 위반된다라고 하며 해당 요청을 받아들일 수 없다라고 알려준다.
즉, SOP는 동일 출처에서의 Request만 받아들이기 때문에 보안에 있어 큰 강점을 보여준다.

하지만 또 문제가 있다, 다른 출처의 리소스가 필요한 경우가 있는데 이 때 사용하는 것이 CORS이다.

CORS란?

Cross Origin Resource Sharing
다른 출처의 자원을 공유

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

CORS 접근제어 시나리오

  • Preflight Request
  • Simple Request
  • Credentialed Request

Preflight Request

본 요청을 보내기 전 서버한테 물어보는 Request

만약 친구의 집에 놀러가야하는 상황이라고 생각해보자 친구에게 놀러가기 전에 놀러가도 되는지 확인하지 않는다면 큰 민폐일 수 있다.
때문에 친구에게 물어보고 가야할텐데 이 같은 작업이 Preflight Request라고 생각하면 된다.

  1. OPTION메서드를 통해 다른 도메인의 리소스에 요청이 가능한지 확인한다.
  2. 요청이 가능하다면 실제 요청(Actual Request)를 보낸다.

요청을 하게 되면 2번의 요청이 보내지게 된다.

처음에는 Prefligth Request 즉, 해당 요청을 보내도 되는지 확인하고 Actual Request를 보낸다.

만약에 Preflight Request가 거부됐다면 Actual Request는 실제로 보내지는 않는다.

Preflight Request를 할 때 물어볼 사항들이 있고 거기에 맞는 포맷이 있다.

OPTION /doc HTTP/1.1
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

맨 처음 헤더에는 Origin이 들어있어야 한다.
즉, 이 요청을 어디서부터 날라 가는거다 라고 표현을 해야하고 다음 Access-Control-Request-Method는 실제 요청의 메서드 즉, 나는 이 메서드를 보낼건데 이 메서드를 보내도 되는지 물어보는 것이고 Actual Request의 메서드를 알려줘야 합니다.

이렇게 클라이언트가 요청을 보내면 서버에서 답변이 올텐데 이는 아래처럼 생겼다.

Access-Control-Allow-Origin: http:foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
  1. Access-Control-Allow-Origin은 서버측에서 해당 Origin은 허가가 되어있다고 보내주는 것이다.
  2. Access-Control-Allow-Methods는 해당 Method들이 허가 되어있다고 알려주는 것이다.
  3. Access-Control-Allow-Headers는 해당 Header들이 허가 되어있다고 알려주는 것이다.
  4. Access-Control-Max-Age는 응답 캐시기간을 의미한다.

Preflight Response가 가져야 하는 특징

  1. 응답 코드는 200대여야 한다.
  2. 응답 Body는 비어있는 것이 좋다.

Simple Request

Preflight Request와는 다르게 바로 본 요청을 보내며 CORS인지 확인하는 요청방식

Simple Request를 위한 조건

  1. GET, POST, HEAD 메서드를 사용해야한다.
  2. Content-Type이 아래 세가지 중에 하나여야 한다.
    A. application/x-www-form-unlencoded
    B. multipart/form-data
    C. text/plain
  3. 헤더는 Accept, Accept-Language, Content-Language, Content-Type만 허용된다.

예시

위와 같은 상황이라면 요청을 보낸 클라이언트의 Origin과 서버에서 허가하는 Origin이 다르니 cross origin에러가 발생할 것이다.

그렇다면 여기서 의문이 든다. 한번만 요청하기 때문에 자원이 절약되는데 굳이 Preflight Request를 사용하는 이유는 무엇일까?

Preflight Request를 사용하는 이유

CORS를 모르는 서버를 위해

만약 Simple RequestCORS를 모르는 서버에게 보낸다고 생각해보자

클라이언트는 브라우저를 통해 특정 메서드로 요청을 보낼텐데 서버는 CORS를 모르기 때문에 일단 요청을 처리하고 응답하게 되는데 당연히 응답해야 할 Access-Control-Allow-Origin이 없을 것이다.
그렇다면 그제서야 브라우저가 서버는 Access-Control-Allow-Origin이 없기 때문에 cross-origin에러를 발생시킵니다.

여기서 문제점은 서버가 일단 요청을 처리한다는 것이다. 위의 예제는 GET메서드니 큰 일은 없겠지만 PATCHDELETE같은 데이터의 변경이 일어나는 메서드라면 큰 문제가 된다. CORS에러가 발생했음에도 이미 서버는 요청을 모두 처리했기 때문에 지우면 안되는 데이터까지 수정하게 되기 때문이다.

하지만 Preflight Request를 사용하게 된다면 해당 요청은 사전 요청이기 때문에 서버는 어떠한 행동도 취하지 않는다.
때문에 서버가 안전하게 요청을 주고 받을 수 있다.

CORS 해결하기

여러 해결방법이 있지만 주로 사용하는 Proxy 서버를 설정해서 해결하는 방법을 주로 다룰 것이다.

해당 해결방법은 Front Server에서 특정 query string으로 요청을 보내면 포트를 살짝 바꿔서 보내는 방식이다.

아래의 예제는 Webpack dev-server를 사용한다면 사용해볼 수 있는 설정이다.

proxy: {
  '/todos': {
    target: 'http://localhost:7500/todos',
      pathRewrite: { '^/todos': '' }
  }
}
profile
프론트엔드 개발자

1개의 댓글

comment-user-thumbnail
2023년 1월 20일

Plugin 이나 Iframe 에서 프록시서버가 필수적인 경우가 있습니다. 이때는 https://cors.sh 와 같은 프록시 서비스를 이용할수 있습니당 :)

답글 달기