CORS 정리

rkdghwnd·2023년 5월 31일
0

Origin이란?


URI에서 Protocol + Hostname + Port 를 Origin으로 칭함
뒤에 붙는 Path나 Query, Fragment 등이 달라도 Protocel,Hostname,Port가 같으면 같은 Origin이다.

최신 브라우저가 지원하는 데이터 공유 정책

최신 브라우저는 HTTP 환경에서 무분별한 데이터 공유를 차단하고 보안을 강화하기 위해
브라우저 자체적으로 데이터 공유에 대한 정책을 시행하고, 메시지를 주고받는 과정에서
브라우저가 자원공유를 차단하고 허용하고 있다.

Same Origin Policy(SOP)

같은 오리진끼리만 데이터를 송수신하도록 허용하는 정책
서로 다른 출처가 무분별하게 데이터를 주고받으면 해커가 정보를 탈취할수 있는 위험이 생긴다.
브라우저는 SOP 정책을 통해 서로 다른 오리진에 대해 데이터 공유를 차단한다.

Cross Origin Resource Sharing(CORS)

교차 출처(Cross Origin)에 대한 자원 공유 정책
여기서 교차 출처가 의미하는 것은 서로 다른 오리진으로,
서로 다른 오리진에 대해 자원을 공유할지를 결정하는 정책이다.
SOP가 서로 다른 오리진의 데이터 공유를 차단하는 정책이라면, CORS는 허용하는 정책

ex) fetch, xmlHttpRequest, axios 같은 ajax 기능들은 SOP 정책에 의거해
서로 다른 오리진의 데이터 송수신을 차단한다. 이 과정에서 CORS 에러가 브라우저에 나타나고
에러를 해결하기 위해서 CORS 설정을 허용해 서로 다른 오리진의 자원공유를 허용할 수 있다.

CORS가 검증되는 과정

1.브라우저 사이에서 요청(request)과 응답(Response)이 이루어진다.(이루어지는 시나리오는 크게 3가지)
2.브라우저는 요청의 header에 있는 origin 속성과, 응답의 header에 있는 access-control-allow-origin 속성을 비교한다.
3. origin이 같으면 응답 허용(허용된 교차출처가 아닌 상태), 아니면 차단

CORS를 허용하는 방법

구체적인 방법은 언어나 프레임워크 등에 따라 달라지기 때문에 공식문서를 참고하는 것을 권장

  1. 백엔드에서 CORS 설정을 허용한다.
  • ex) access-control-allow-origin 설정에 origin을 등록하기
  • ex) node.js 기준 cors 모듈을 사용하고 세부속성으로 origin 등록하기
  1. 자격인증정보(쿠키, authorization header 등)를 주고 받는 경우 요청과 응답헤더에 credentials 관련 속성을 true로 지정해 준다.
    ex) axios 헤더 속성으로 withCredentials 속성 true 로 설정하기
    ex) cors 모듈을 사용할경우 credentials 속성 true 로 설정하기
// fetch 메서드
fetch("https://example.com:1234/user", {
	method: "POST",
	credentials: "include", // 클라이언트와 서버가 통신할때 자격인증정보를 공유하겠다는 설정
    body: JSON.stringify({
        userId: 1,
    }),
})
// axios 라이브러리
axios.post('https://example.com:1234/users/login', { 
    profile: { username: username, password: password } 
}, { 
	withCredentials: true // 클라이언트와 서버가 통신할때 자격인증정보를 공유하겠다는 설정
})
// jQuery 라이브러리
$.ajax({
	url: "https://example.com:1234/users/login",
	type: "POST",
	contentType: "application/json; charset=utf-8",
	dataType: "json",		
	xhrFields: { 
    	withCredentials: true // 클라이언트와 서버가 통신할때 자격인증정보를 공유하겠다는 설정
    },
	success: function (retval, textStatus) {
		console.log( JSON.stringify(retval));
	}
});
// node.js에서 백엔드 cors 모듈 사용하는 경우
app.use(
  cors({
    origin: process.env.FRONT_END_DOMAIN,
    credentials: true,
  })
);

CORS 작동 방식에 따른 3가지 시나리오

  1. Simple Request
    예비 요청(Preflight Request)를 생략하고 바로 본 요청을 보내고 응답하는 방식
    3가지 조건을 모두 만족해야 Simple Request가 성립한다.
  • 요청 메소드는 GET, HEAD, POST 중 하나여야 한다.
  • Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 헤더 여야 한다.
  • Content-Type 헤더가 application/x-www-form-urlencoded, multipart/form-data, text/plain중 하나여야한다

Simple Request가 성립하기 위해서는 까다로운 조건이 많고, 대부분의 HTTP API 요청은 text/xml 이나 application/json으로 통신하기 때문에
3번째 Content-Type이 위반된다. 따라서 대부분의 API 요청은 Preflight Request가 많다.

  1. Preflight Request
    Simple Request의 조건을 벗어나는 경우, Preflight Request를 보내게 된다.
    Preflight Request는 본 요청을 보내기 전, 브라우저 스스로 안전한 요청인지 미리 확인하기 위해 보내는 요청으로
    OPTION 메소드를 사용하여 요청을 보내고 서버에서는 CORS 관련 속성을 포함한 header 정보를 브라우저로 응답한다.
    브라우저에서는 요청과 응답정보를 비교해 정책에 따라 응답을 차단하거나 허용한다(이 지점에서 요청의 origin과 응답의 access-control-allow-origin 비교)
    정책에 부합하는 경우 본 요청을 보내 데이터를 송수신하도록 한다.

  2. credentialed Request
    클라이언트가 백엔드로 요청을 보낼 때 자격인증정보(Credential)을 포함해 보낼때 사용되는 방식이다.
    여기서 credential 정보란 쿠키나 Authorization 헤더에 토큰값을 포함해 보내는 방식(JWT)등을 말한다.
    일반적으론 credential을 허용하는 설정을 하지 않으면 브라우저는 credential 정보가 송수신 되는것을 차단하여
    요청자체는 보내지지만 credential을 제외한 요청이 서버에게 전달되는 현상이 일어난다.

profile
rkdghwnd's dev story

0개의 댓글