Same-Origin Policy 동일 출처 정책과 CORS 에러

yejinh·2020년 1월 7일
4

동일 출처 정책 Same-Origin Policy

동일 출처 정책(same-origin policy)은 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식입니다. 동일 출처 정책은 잠재적으로 해로울 수 있는 문서를 분리해, 공격받을 수 있는 경로를 줄입니다.
출처 MDN

즉, 동일 출처 정책은 웹 브라우저 보안을 위해 프로토콜, 호스트, 포트가 동일한 서버로만 ajax 요청을 주고 받을 수 있도록 한 정책이다.

 

그렇다면 프로토콜, 호스트, 포트는 무엇인가?

현재 페이지의 프로토콜과 호스트, 포트를 확인하는 방법은 간단하다. 개발자 도구를 열고 locataion을 입력하면

위와 같이 해당 브라우저 페이지의 프로토콜, 호스트, 포트와 함께 '브라우저 위치 혹은 주소'와 관련된 정보를 확인할 수 있다.

다시 말해 동일 출처 정책이란 같은 Origin 출처의 서버로만 요청을 주고 받을 수 있다는 것이다.

  • http://www.same-domain.com/ --> http://www.same-domain.com = same-origin

  • http://www.same-domain.com -/-> http://www.cross-domain.com = cross-origin

여기서 origin이란 요청을 보낸 도메인을 의미한다

예를 들어 클라이언트를 3000포트, 서버를 8080포트에서 실행한 후에 클라이언트에서 서버로 AJAX요청을 보내면 어떻게 될까?

XMLHttpRequest cannot load 'http://localhost:3000'. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.

같은 도메인으로 요청을 보낸 것이 아니기 때문에 동일 출처 정책에 의해 어김없이 위와 같은 에러를 마주하게 된다.

보안을 위한다고 하지만 개발을 하다보면 외부 API를 사용하는 경우도 많고 클라이언트와 서버를 분리하여 개발하는 경우도 많아 해당 정책으로 인한 불편함과 함께 매우 빈번하게 겪게 되는데,,

이러한 불편을 해결하기 위해 등장한 것이 Cross-Origin Resource Sharing(CORS) 정책이다.

 

Cross-Origin Resource Sharing(CORS)

Cross-Origin Resource Sharing(CORS) 은 추가 HTTP 헤더를 사용하여 브라우저가 한 출처에서 실행중인 웹 애플리케이션에 선택된 액세스 권한을 부여하도록하는 메커니즘입니다. 다른 출처의 자원. 웹 응용 프로그램은 자체와 다른 출처 (도메인, 프로토콜 또는 포트)를 가진 리소스를 요청할 때 cross-origin HTTP 요청을 실행합니다.
출처 MDN

즉, Same-Origin Policy의 문제점을 해결하기 위한 정책인 만큼 CORS란 cross-Origin 즉, 출처가 다른 도메인에서의 AJAX요청이라도 서버 단에서 데이터 접근 권한을 허용하는 정책이다.

 

서버 단 해결 방법

결국 cors에러가 발생하는 원인은 클라이언트와 서버의 도메인이 달랐을 때 보안 상의 이유로 응답을 받지 못하도록 막은 것이므로 서버 단에서 특정 도메인 혹은 모든 도메인을 허용하도록 설정만 해주면 된다.

1. Access-Control-Allow-Origin response header

Access-Control-Allow-Origin 응답 헤더는 이 응답이 주어진 origin으로부터의 요청 코드와 공유될 수 있는지를 나타냅니다.
출처 MDN

서버측 응답에서 접근 권한을 주는 헤더를 추가해 cors 에러를 해결하는 방법.

app.use((req, res, next) => {
    res.header("Access-Control-Allow-Origin", "*"); // 모든 도메인
    res.header("Access-Control-Allow-Origin", "https://example.com"); // 특정 도메인
});

이외에도 아래와 같은 헤더를 추가할 수 있다.

Access-Control-Allow-Method
Access-Control-Max-Age
Access-Control-Allow-Headers

허나 이는 접근 권한을 허용하는 모든 요청의 응답에 추가해주어야 하므로 만약 모든 요청에 권한 허용을 한다면 모든 응답 전에 위의 헤더를 추가해야 한다는 번거로움이 있다.

 

2. Node.js Express 미들웨어 CORS

미들웨어 cors는 대부분의 경우에 내가 사용했던 방식이다. 문서가 매우 친절하게 되어있어 미들웨어 사용 및 옵션 설정이 어렵지 않다.

npm i cors --save
const express = require('express');
const cors = require('cors');
const app = express();
 
app.use(cors());

문서에 따라 미들웨어를 설치를 하고 App.js에 미들웨어를 꽂으며 세팅 완료.

위와 같이 아무런 옵션없이 app.use(cors());로 설정한다면 모든 cross-origin 요청에 대해 응답을 해주게 되는데, 만약 모든 요청에 응답하는 것이 아닌 보안상 특정 도메인 요청만 받아야 하는 경우 혹은 특정 요청에만 응답하는 경우에는 그에 따른 옵션들을 다양하게 설정해 줄 수 있다.

특정 도메인 접근 허용

const corsOptions = {
  origin: 'http://example.com', // 접근 권한을 부여하는 도메인
  credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가해줌
  optionsSuccessStatus: 200 // 응답 상태 200으로 설정 
};

app.use(cors(corsOptions));

 
특정 요청 접근 허용

app.get('/products/:id', cors(), function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for a Single Route'})
});

이외에도 위에서 언급한 Access-Control-* 헤더 설정 방식처럼 특정 method 등을 옵션으로도 설정할 수도 있다.

 

클라이언트 단 해결 방법

만약 직접 구현한 서버가 없어 클라이언트 내에서만 해결을 해야한다면 어떤 방식을 사용할 수 있을까?

proxy 설정

프록시란 ?

다양한 이유로 (주로 보안의 문제로) 직접 통신하지 못하는 두 개의 컴퓨터 사이에서 서로 통신할 수 있도록 돕는 역할을 가리켜 프록시라 일컫는다.

클라이언트 - 서버 중간에서 요청을 받아 Access-Control-Allow-Origin헤더를 추가해주는 중간 다리 역할을 놔주는 방법으로 해결할 수 있다.

https://cors-anywhere.herokuapp.com

위의 주소를 요청 url앞에 적어두고 요청을 보내는 것이다.

실제로 meetup 로그인을 구현하며 사용했던 방식인데, AJAX 요청이 프록시 서버를 건너며 Access-Control-* 설정이 되어 요청에 대한 응답을 받을 수 있었다.

 
이외에도 직접 proxy를 만들거나 JSONP 요청을 보내 스크립트 파일을 호출하여 우회.. 등 다양한 방법들이 있다.

 


정말 거짓말처럼 이 글을 작성하는 동안에 배포 해 둔 프로젝트에서 cors에러가 발생해서 놀랬다..

cors 미들웨어를 사용하여 뚫어주는 방법으로 모든 도메인에 오픈시켜 놓았었는데 어제까지는 문제없이 잘 되던 것이 알 수 없는 이유로 갑작스레 cors에러가 떴다. 아니 다 오픈했는데?!!

잠시 당황해서 클라이언트 코드도 살폈는데 어째든 서버 단에서 cors를 뚫어 놓았기 때문에 해결도 서버 단에서 할 수 있었다. 아무튼 덕분에 cors option 설정해서 클라이언트 도메인만 넣어 보안 강화도 시키고 이후 재배포를 했더니 다시 잘 동작한다. 하.. 심장뛰어.. 흥미로워..

문득 유지 보수란 시간을 내어 하는 것이 아니라 일과가 되어야 한다는 말이 떠오른다.

profile
꿈꿀 수 있는 개발자가 되고 싶습니다

1개의 댓글

comment-user-thumbnail
2020년 4월 14일

도움받고갑니다 ~

답글 달기