[NodeJS] cors 미들웨어

nakkim·2022년 4월 15일
0
post-custom-banner

npm i cors

cors는 CORS(Cross-origin resource sharing)를 활성화하는데 사용하는 Connect/Express 미들웨어이다.

CORS

(Cross-origin resource sharing)

한줄요약

서로 다른 도메인 간 리소스 요청을 처리하는 메커니즘이다.

<img> 태그로 다른 도메인의 이미지 파일을 가져오거나 <link> 태그로 CSS를 가져오는 등의 요청은 기본적으로 다른 도메인 간에서도 가능하다.

하지만 스크립트 부분(<script> 태그)에서 생성된 ‘교차 도메인’ 요청(특히 Ajax 요청)은 same-origin security policy에 의해 기본적으로 금지된다.

CORS는 브라우저와 서버가 상호작용하여 교차 오리진 요청을 허용하는 것이 안전한지 결정하는 방법을 정의한다.

origin

웹 콘텐츠의 출처(origin)는 접근할 때 사용하는 URL의 스킴(프로토콜, 호스트(도메인), 포트로 정의된다.

두 객체의 스킴, 호스트, 포트가 모두 일치하는 경우 같은 출처를 가졌다고 말한다.

일부 작업은 동일 출처 콘텐츠로 제한되나, CORS를 통해 제한을 해제할 수 있다.

개요

웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행한다.

웹 브라우저에서 해당 정보를 읽는 것이 허용된 출처라는 것을 서버에 설명할 수 있는 새로운 HTTP 헤더를 추가함으로써 동작한다.

  • Access-Control-Allow-Origin 헤더
  • Access-Control-Allow-Origin: * : 모든 클라이언트의 요청을 허용
  • 브라우저에서 서버로 요청을 보낼 때만 필요(서버 - 서버, 브라우저 외의 클라이언트 - 서버는 그냥 가능)

서버 데이터에 변경을 일으키는 요청인 Ajax 및 HTTP 요청 메서드(보통 GET 이외의 메서드 또는 특정 MIME 유형의 POST)의 경우

→ 브라우저가 요청을 “preflight”하여 OPTIONS 메서드로 서버에 요청을 보냄(이 도메인을 허용하니?)

→ 서버에서 승인하면, 실제 요청을 전송

서버는 인증 정보(쿠키 및 HTTP 인증 데이터 포함)를 요청과 함께 전송해야 하는지를 클라이언트에 알릴 수 있다.

  • preflight(사전 요청)
    • 본격적인 교차 출처 HTTP 요청 전에 서버 측에서 그 요청의 메서드와 헤더에 대해 인식하고 있는지를 체크한다.
    • HTTPMethod("OPTIONS") 요청이다.
      • 목표 리소스와의 통신 옵션을 설명하기 위해 사용
      • HTTPHeader("Access-Control-Request-Method"), HTTPHeader("Access-Control-Request-Headers"), HTTPHeader("Origin") 총 3가지의 HTTP request headers를 사용
  • preflight를 트리거하지 않는 요청
    • 다음 중 하나의 메서드
      • [GET](https://developer.mozilla.org/ko/docs/Web/HTTP/Methods/GET)
      • [HEAD](https://developer.mozilla.org/ko/docs/Web/HTTP/Methods/HEAD)
      • [POST](https://developer.mozilla.org/ko/docs/Web/HTTP/Methods/POST)
    • 유저 에이전트가 자동으로 설정 한 헤더 (예를들어, [Connection](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Connection)User-Agent (en-US)Fetch 명세에서 “forbidden header name”으로 정의한 헤더)외에, 수동으로 설정할 수 있는 헤더는 오직 Fetch 명세에서 “CORS-safelisted request-header”로 정의한 헤더 뿐이다.
      • [Accept](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Accept)
      • [Accept-Language](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Accept-Language)
      • [Content-Language](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Content-Language)
      • [Content-Type](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Content-Type) (아래의 추가 요구 사항에 유의)
    • [Content-Type](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Content-Type) 헤더는 다음의 값들만 허용
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
    • 요청에 사용된 XMLHttpRequestUpload 객체에는 이벤트 리스너가 등록되어 있지 않다. 이들은 [XMLHttpRequest.upload](https://developer.mozilla.org/ko/docs/Web/API/XMLHttpRequest/upload) 프로퍼티를 사용하여 접근한다.
    • 요청에 [ReadableStream](https://developer.mozilla.org/ko/docs/Web/API/ReadableStream) 객체가 사용되지 않는다.
  • 동작 예시 https://developer.mozilla.org/ko/docs/Web/HTTP/CORS#접근제어시나리오_예제

Usage

Access-Control-Allow-Origin 헤더를 추가해줘야 CORS 문제를 해결할 수 있다.

res.set 메서드로 직접 넣어도 되지만, 이를 대신 해주는 cors 모듈을 사용하면 편하다.

모든 CORS 요청 허용

import cors from 'cors';
...
app.use(cors());

단일 경로에 대해 CORS 허용

import cors from 'cors';
...
app.get('/경로', cors(), (req, res) => res.json({msg: 'CORS-enabled'}));

Configuring CORS

const corsOptions = {
  origin: 'http://example.com',
  optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
};

app.use(cors(corsOptions));

Configuring CORS w/ Dynamic Origin

origin 옵션에 제공된 함수를 사용하여 오리진을 동적으로 인증할 수 있다.

  • 데이터베이스와 같은 백업 데이터 소스에서 허용된 오리진의 동적 로드를 허용하도록 설계
  • 오리진(없는 경우 undefined)과 시그니처 callback(error, origin) 을 가진 콜백을 전달
  • 콜백에 대한 origin 인수는 미들웨어의 오리진 옵션에 허용된 모든 값이 가능(함수 제외)
const corsOptions = {
  origin: function (origin, callback) {
    // db.loadOrigins is an example call to load
    // a list of origins from a backing database
    db.loadOrigins(function (error, origins) {
      callback(error, origins)
    })
  }
};

Enabling CORS Pre-Flight

특정 CORS 요청은 복잡한 것으로 여겨지며 초기 OPTIONS 요청(aka pre-flight request)이 필요하다.

복잡한 요청의 예

  • GET/HEAD/POST 이외의 HTTP 동사를 사용(DELETE 등)
  • 사용자 지정 헤더 사용

pre-flight를 활성화하려면 지원할 경로에 OPTIONS 핸들러를 추가해야 한다.

app.options('/지원할/경로', cors()); // enable pre-flight request for DELETE request
app.del('/지원할/경로', cors(), (req, res) => {
  ...
});

또는 모든 경로에 대해 활성화 가능

app.options('*', cors()); // include before other routes

NOTE: 이 미들웨어를 애플리케이션 수준 미들웨어(예: app.use(cors))로 사용할 때는 모든 경로에 대해 pre-flight 요청이 처리된다.

Configuring CORS Asynchronously

const allowlist = ['http://example1.com', 'http://example2.com'];
const corsOptionsDelegate = function (req, callback) {
  const corsOptions;
  if (allowlist.indexOf(req.header('Origin')) !== -1) {
    corsOptions = { origin: true }; // reflect (enable) the requested origin in the CORS response
  } else {
    corsOptions = { origin: false }; // disable CORS for this request
  }
  callback(null, corsOptions); // callback expects two parameters: error and options
};

cors를 사용해보자

const getOrigin = () => {
  const origin = [process.env.URL_CLIENT, process.env.URL_ADMIN];
  if (process.env.NODE_ENV === 'production') {
		origin.push(process.env.URL_CLIENT_OLD);
	}
	return origin;
};
...
app.use(cors({ origin: getOrigin(), credentials: true }));

credentials? - 도메인 간 쿠키 공유

Access-Control-Allow-Credentials CORS 헤더를 구성한다.

  • Access-Control-Allow-Credentials? 응답헤더 Access-Control-Allow-Credentials 는 요청의 자격증명 모드([Request.credentials](https://developer.mozilla.org/ko/docs/Web/API/Request/credentials))가 "include" 일때, 브라우저들이 응답을 프로트엔드 자바스트립트 코드에 노출할지에 대해 알려준다. 요청의 자격증명 모드가 "include" 이고, Access-Control-Allow-Credentials 값이 true 일 경우에만 브라우저는 프로트엔드 자바스트립트에 응답을 노출한다. (자격증명 - 쿠키, authorization 헤더, TLS 클라이언트 인증서)

헤더를 전달하려면 true 로 설정(아니면 헤더가 생략됨)

profile
nakkim.hashnode.dev로 이사합니다
post-custom-banner

0개의 댓글