[NodeJs] Cors 처리하기
Cors 사용법
Cors 란
CORS(Cross-Origin Resource Sharing)는 출처가 다른 자원들을 공유한다는 뜻으로, 한 출처에 있는 자원에서 다른 출처에 있는 자원에 접근하도록 하는 개념입니다.
예를 들어 https://onstory.fun:443/nodejs 에서 https(Protocol), onstory.fun(Host), 443(Port) 이 세가지가 동일하면 동일출처(Origin) 이라고 합니다.
채팅서비스를 구성한다고 할때 Application과 소켓은 보통 분리되는데 가령 https://onstory.fun 에서 채팅창이 보이고 실제 체팅은 node서버인 https://node.onstory.fun 에서 처리한다고 하면 Host 가 달라서 Origin에 위배 됩니다.
다른 출처 가령 http://anydomain.com 에서 https://node.onstory.fun 를 마음대로 제어 한다면 보안상 문제가 발생하겠죠? 이를 막기위해 브라우저는 기본적으로 cors 정책을 따르게 됩니다.
동일 출처 정책(Same-origin policy)
동일한 출처(Protocol, Host, port) 가 같은 경우에만 리소스에 접근가능
Cross-origin policy
Same-origin과 반대되는 개념으로 다른 출처에서도 접근 가능하게 하는 정책입니다.
아래 3가지가 Cross-origin policy 에 속합니다.
1. 단순요청(Simple Request)
const xhr = new XMLHttpRequest();
const url = 'https://response.server?q=query';
xhr.open('GET', url);
xhr.onreadystatechange = requestHandler;
xhr.send();
GET, HEAD, POST 요청만 가능합니다
-
request : 자신의 url을 origin에 담아서 response 서버 쪽으로 요청
GET https://response.server?q=query
Origin: https://request.server -
response: request에 허용되었다는 정보를 전달, access-control-allow-origin 이 * 일때는 모든 출처를 허용하지만 특정 도메인을 지정할 경우는 특정 도메인만 됩니다.
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://response.server
-
만약 response 서버에서 origin을 허용하지 않을 경우는 'No Access-Congtrol-Allow-Origin' 이라는 에러는 발생시킵니다.
Access to XMLHttpRequest at 'http://localhost:9000/socket.io/?EIO=4&transport=polling&t=OqwIL00' from origin 'http://localhost:4200' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://192.168.0.21:4200' that is not equal to the supplied origin.
GET http://localhost:9000/socket.io/?EIO=4&transport=polling&t=OqwIL00 net::ERR_FAILED 200 (OK)
2. 프리 플라이트(Preflight Request)
const xhr = new XMLHttpRequest();
const url = 'https://response.server?q=query';
xhr.open('GET', url);
xhr.setRequestHeader('custom-header', 'test')
xhr.onreadystatechange = requestHandler;
xhr.send();
Simple Request 는 쿼리를 보내고 결과값을 받거나 에러를 받거나 둘 중 하나인 반면 Preflight Request 는 먼저 response.server 측에 내가 쿼리를 보내도 될까요? 라는 요청을 먼저 보내고 '응 그래 결과를 주겠어' 라는 답을 받으면 실제 요청을 합니다.
-
Pre-flight request
OPTIONS https://response.server?q=query
Access-Control-Request-Method : GET // 실제 요청이 보낼 HTTP 메서드 Access-Control-Request-Headers : custom-header ... // 실제 요청에 포함된 header Origin: https://request.server -
Pre-flight response: request에 허용되었다는 정보를 전달, access-control-allow-origin 이 * 일때는 모든 출처를 허용하지만 특정 도메인을 지정할 경우는 특정 도메인만 됩니다.
HTTP/1.1 204 No Content Access-Control-Allow-Origin : https://request.server // 서버가 허용하는 출처 Access-Control-Allow-Methods : GET // 서버가 허용하는 HTTP 메서드 리스트 Access-Control-Allow-Headers : .... // 서버가 허용하는 header 리스트 Access-Control-Max-Age : 6000 // 프리 플라이트 요청의 응답을 캐시에 저장하는 시간
-
위에서 처럼 request 에서는 바로 GET이나 POST로 데이타를 전송하는 것이 아니라 OPTIONS에 요청할 정보를 보내고 Pre-flight response 받은 서버에서 허락하는 조건이 일치하면 아래처럼 실제적인 query가 시작됩니다.
-
request : 자신의 url을 origin에 담아서 response 서버 쪽으로 요청
GET https://response.server?q=query
Origin: https://request.server -
response: request에 허용되었다는 정보를 전달, access-control-allow-origin 이 * 일때는 모든 출처를 허용하지만 특정 도메인을 지정할 경우는 특정 도메인만 됩니다.
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://response.server
3. 신용 요청(Credentialed Request)
신용 요청은 쿠키, 인증 헤더, TLS 클라이언트 인증서 등의 신용정보와 함께 요청합니다. 기본적으로, CORS 정책은 다른 출처 요청에 인증정보 포함을 허용하지 않습니다. 요청에 인증을 포함하는 플래그가 있거나 access-control-allow-credentials가 true로 설정 한다면 요청할 수 있습니다.
const xhr = new XMLHttpRequest();
const url = 'https://response.server?q=query';
xhr.open('GET', url);
xhr.withCredentials = true;
xhr.send();
-
request : 자신의 url을 origin에 담아서 response 서버 쪽으로 요청
GET https://response.server?q=query
Origin: https://request.server Cookie: ...... -
response: request에 허용되었다는 정보를 전달, access-control-allow-origin 이 * 일때는 모든 출처를 허용하지만 특정 도메인을 지정할 경우는 특정 도메인만 됩니다.
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://response.server Access-Control-Allow-Credentials: true
NodeJs에서 CORS 처리하기
버젼마다 사용하는 방법이 약간 다른데 여기서는 ver > 3.0 에 대해 설명 드리겠습니다.
- io를 3000번에서 직접 리슨하는 경
const server = require('http').createServer(function() { // req, res
});
const io = require('socket.io')(server, {
transports: ['websocket', 'polling'],
// allowEIO3: true,
cors: {
origin: 'http://localhost:8100',
methods: ['GET', 'POST'],
credentials: true
}
}).listen(3000);
- io를 http 서버를 통하여 리슨하는 경우
require('dotenv').config();
const server = require('http').createServer(function() { // req, res
}).listen(3000);
const io = require('socket.io')(server, {
transports: ['polling'],
allowEIO3: true,
cors: {
origin: 'http://localhost:8100',
methods: ['GET', 'POST'],
credentials: true
}
});
- express 같이 사용하는 경우
const express = require('express');
const app = express();
const server = app.listen(process.env.DICE_PORT);
const io = require('socket.io')(server, {
transports: ['polling'],
// allowEIO3: true,
cors: {
origin: 'http://localhost:8100',
methods: ['GET', 'POST'],
credentials: true
}
}); // 'polling'"
cors deault"{
"origin": "*",
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"preflightContinue": false,
"optionsSuccessStatus": 204
}"