출처
란?다음과 같은 URI에서 https
는 프로토콜, www.naver.com
는 호스트 앞의 주소에서는 포트가 생략 되었지만 https
의 기본 포트는 443으로 https://www.naver.com:443
으로 이동하였을 때 똑같은 화면이 나오는 것을 확인해 볼 수 있다. 이처럼 프로토콜 + 호스트 + 포트까지의 조합을 출처라고 한다.
SOP은 같은 출처의 리소스끼리만 공유가 가능하다는 동일 출처 정책으로 SOP을 통해 잠재적으로 해로울 수 있는 문서를 분리해 공격받을 수 있는 경로를 줄여 준다.
하지만 우리가 일상생활에서 다른 출처의 리소스를 받게 될 일은 무수히 많다. 개발을 위해 api를 활용한다거나 정보를 받아올 일이 생긴다면 다른 출처에 접근해야 한다.
모든 브라우저는 SOP을 사용하고 있는데 어떻게 하면 다른 출처에 접근해 다른 리소스를 받아올 수가 있을까? 이 상황에서 필요한 것이 바로 CORS
다! CORS
대해서 자세히 알아보자.
추가적인 HTTP 헤더를 사용해 한 출처에 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다.
브라우저는 SOP을 기본적으로 사용하므로 다른 리소스의 공유가 불가능한 상태인데 CORS를 사용하면 접근 권한을 부여받는 다는 것이다!
위 사진은 SOP에 의해 접근이 불가능하여 나오는 CORS 에러이다. CORS 설정을 통해 서버 응답 헤더(HTTP 헤더)에 ‘Access-Control-Allow-Origin’을 작성하면 접근 권한을 얻을 수 있다고 나와있다. 에러는 즉 CORS 때문이 아닌 SOP 정책에 의한 에러이다. 이를 해결하기 위해서 CORS 설정을 통해 해결할 수 있다.
실제로 요청을 보내기 전에
OPTIONS
메서드를 통해 사전 요청을 보내서 해당 출처 리소스에 접근 권한이 있는지부터 확인하는 방식
특정 조건이 만족되면 Preflight 요청을 생략하고 요청을 보내는 것
GET
, HEAD
, POST
요청 중 하나Accept
, Accept-Language
, Content-Language
, Content-Type
헤더의 값만 수동으로 설정할 수 있다.Content-Type
헤더에는 application/x-www-form-urlencoded
, multipart/form-data, text/plain
값만 허용요청 헤더에 인증 정보를 담아서 보내는 요청, 출처가 다를 경우에 별도의 설정을 하지 않으면 쿠키를 보낼 수 없다. (프론트, 서버 모두 CORS 설정을 해야 한다.)
withCredentials : true
Access-Control-Allow-Credentials : true
서버 측에서 Access-Control-Allow-Origin
를 설정할 떄 와일드 카드 *
를 넣을 시에는 모든 출처를 허용되기 때문에 에러가 발생한다. 인증 정보를 다룰 때에는 출처를 정확하게 설정해 주자.
const http = require('http');
// 서버 만들기
const server = http.createServer((request, response) => {
// response Header 설정, 모든 도메인
response.setHeader("Access-Control-Allow-Origin", "*");
// response Header 설정, 특정 도메인
response.setHeader("Access-Control-Allow-Origin", "https://google.com");
// response Header 설정, 인증 정보를 포함한 요청을 받을 경우
response.setHeader("Access-Control-Allow-Credentials", "true");
})
// 미들웨어 cors 불러오기
const cors = require("cors");
const app = express();
//모든 도메인
app.use(cors());
//특정 도메인
const options = {
origin: "https://codestates.com", // 접근 권한을 부여하는 도메인
credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가
optionsSuccessStatus: 200, // 응답 상태 200으로 설정
};
app.use(cors(options));
//특정 요청
app.get("/example/:id", cors(), function (req, res, next) {
res.json({ msg: "example" });
});