개발 블로그를 쓰는 건 내가 배운 걸 정리하고 싶어서 쓰는 건데
가끔은 내가 이렇게 공부를 하고 있어요 라는 보여주기 식이 되는 것 같다.
그것도 있고, 글 쓰는데 시간이 너무 오래 걸리다보니 정리하고 싶은 것들은 있는데 적지 못한 것들이 많았다.
... 아무튼 여러 이유로 블로그를 아예 처음부터 시작해보려고 한다😃
CORS라는 단어에 대해 알고는 있었는데 정작 오류가 발생하니까 알아보게 되었다.
(근데 이렇게 배우는 게 제일 기억에 남는 듯)
CORS란 무엇인가?
MDN에선 해당 단어에 대해 이렇게 말하고 있다.
CORS는 다른 출처(도메인, 스키마, 포트)를 가지는 서버에 대해 리소스를 로드하는 것을 허용하는 메니즘에 기반한 HTTP 헤더이다. 또한 CORS는 실제 요청을 허용하는지 확인하기 위해 브라우저가 교차-출처 리소스를 호스팅하는 서버에 "preflight" 요청을 하는 메커니즘에 의존한다.
출처로 번역된 origin은 protocol(https://) + host(www.domain.com) + port(:8080) 을 합친 URL을 뜻한다.
JavaScript에서 window.location.origin 을 출력할 때도 해당 URL을 출력한다.
또, preflight는 서버에 실제로 요청을 하기 전 OPTIONS 메서드로 예비 요청을 보내는 것이다.
이런 교차 출처 정책과 함께하는 개념으로는 SOP(Single Origin Policy)가 있는데, 동일한 origin에서의 요청만 허용하는 정책을 뜻한다. 해당 정책이 필요한 이유는 해커가 CSRF(Cross-Site Request Forgery)나 XSS(Cross-Site Scripting) 등의
방법으로 서버 내의 정보를 탈취하는 것을 막기 위함이다. 해당 출처의 검증은 서버에서 하는 것이 아닌 브라우저가 수행하는 것이며, 따라서 fetch를 실행할 때 CORS 에러가 떠도, 서버 단에서는 요청에 대한 응답을 제대로 한 것으로 보여질 수 있다.
CSRF (Cross-Site Request Forgery)
CSRF는 Forgery(사기)라는 단어가 들어간만큼 유저가 보낸 요청을 조작하는 공격 수단이다. 예를 들어, 사용자가 어떤 웹 사이트에 로그인했을 때, 유저 정보와 세션 ID 등이 포함된 쿠키 값을 받는다. 그 후 어떤 이유로 악성 사이트에 접속하게 되면 브라우저에 저장된 쿠키 값과 세션 값을 통해 기존 사이트에 사용자가 원하지 않는 요청을 수행하게 만드는 것이다.
이를 방지하기 위해 서버에선 referrer를 확인하여 요청한 호스트와 referrer가 일치하는 지 확인하거나,
CSRF 토큰등을 사용해 검증하는 방법을 사용한다고 한다.
XSS(Cross-Site Scripting)
XSS는 Cross Site Scripting의 약어지만 어째서인지 줄이면 CSS가 되기 때문에 XSS로 표기하는 것 같다. 웹 사이트에 의도하지 않은 스크립트를 삽입해 사이트를 사용하는 사용자가 웹 사이트에 의도치 않은 요청와 그에 대한 응답을 받고, 공격자에게 사용자 정보가 포함된 응답 데이터를 받아오는 방식이다.
보안을 위한 정책이지만, 작업을 하면서 모든 것을 같은 출처 내에서의 요청으로만 해결할 수는 없다. 그렇기 때문에 CORS가 필요한 것인데, Cross-Origin Resource Sharing 라는 이름과 같이 다른 출처끼리 리소스를 공유할 때 지켜야하는 정책을 의미한다. 해당 정책을 잘 지키면 SOP 정책을 위반해도 CORS 정책에 따라 다른 출처의 리소스를 공유하는 것이 가능하다.
그럼 해당 정책을 지키기 위해선 어떻게 해야하느냐하면, 서버에서 요청에 대한 응답 헤더에 Access-Control-Allow-Origin 속성을 추가하고 리소스 공유를 허용할 도메인을 추가하면 된다. 이후에 브라우저는 사용자의 origin과 서버에서 보내준 Access-Control-Allow-Origin을 확인 후 응답 차단 여부를 결정한다.
Node.js 로 프로젝트를 했을 때는 이런 식으로 CORS 설정을 했었다.
import express from "express";
import cors from "cors";
const app = express();
const corsOptions = {
origin: "http://localhost:3000/",
};
dotenv.config();
app.use(cors(corsOptions));
React로 프로젝트를 했을 때는 이런 식으로 CORS 설정을 했었다.
const api = axios.create({
baseURL: import.meta.env.VITE_BACKEND_URL, //데이터를 요청할 기본 주소
timeout: 3000, // 요청이 timeout보다 오래 걸리면 요청이 중단된다.
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
// 서로 다른 도메인(크로스 도메인)에 요청을 보낼 때 요청에
// credential 정보를 담아서 보낼 지를 결정하는 항목
});