CORS 개념 정리

맘비·2023년 1월 3일
0

CS / Web

목록 보기
1/10

웹 개발을 하다보면 우리가 가져오는 리소스들이 안전한지 검사하게 되는 정책과 마주하게 되는데, SOP(Same Origin Policy)와 CORS(Cross Origin Resource Sharing)가 있다.

이는 서버에 구현된 스펙이 아닌 브라우저에 구현된 스펙이다. 서버는 정책 위반 여부에 관여하지 않는다. 서버가 요청에 응답하고, 응답이 브라우저에 도착하면 브라우저가 그 응답을 분석해 정책 위반 여부를 검사하게 된다. 그래서 서버에서 정상적으로 응답(200 OK)을 하더라도, 정책에 위반되면 브라우저는 그 응답을 처리하지 않고 버리게 된다.

Origin(출처)이란?


URL은 위의 사진과 같이 구성되어 있는데, 그 중에서 Protocol, Host, Port를 합쳐 Origin이라고 한다.

Origin(Protocol, Host, Port)이 일치하면 SOP, 일치하지 않으면 CORS를 따른다.

동일 출처 정책 (Same Origin Policy)


“같은 출처에서만 리소스를 공유할 수 있다” 라는 규칙을 가진 정책이다. 즉 Origin (Protocol, Host, Port)가 동일한지 확인하면 된다. Origin이 같다면 나머지 요소는 다르더라도 같은 출처로 인정된다.

자바 스크립트는 서로 다른 도메인(호스트)에 대한 요청을 보안상 제한하기에, XMLHttpRequest, Fetch API 등 script 태그 내에서는 기본적으로 SOP를 따른다.

✔️ “ https://github.com/about “ 를 기준으로 Same Origin인지 비교해보자.

교차 출처 리소스 공유(Cross Origin Resource Sharing)


추가 HTTP 헤더를 사용해, 한 Origin에서 실행 중인 웹 애플리케이션이 다른 Origin의 선택한 Resource에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. 웹 애플리케이션은 Resource가 자신의 Origin과 다를 때 Cross Origin HTTP 요청을 실행한다.

HTML은 기본적으로 CORS를 따른다. link 태그에서 다른 Origin의 CSS 등의 리소스에 접근하거나, img 태그에서 다른 리소스에 접근하는 것이 가능한 이유이다.

실제 CORS의 3가지 시나리오

  • 예비 요청 (Preflight Request) 브라우저는 요청을 한 번에 보내지 않고, 예비 요청과 본 요청으로 나누어 서버로 전달한다. 예비 요청은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 확인하는 것이다. 이를 Preflight 라고 부르며, 메소드는 OPTIONS가 사용된다. 예비 요청은 보통 PUT, DELETE와 같은 요청을 보낼 때 이용된다. PUT이나 DELETE는 서버의 데이터를 변경하는 요청이기에, 코드 실행 요청을 보내기 전에 예비 요청을 보내 먼저 인증을 하고, 본 요청을 통해 코드 실행 요청을 하는 것이다. 예시)

자바 스크립트의 fetch API를 통해 브라우저에게 리소스를 받아오려는 상황

1. 브라우저가 서버로 예비 요청을 보냄
2. 서버는 정책 정보를 담아서 브라우저로 응답
3. 브라우저는 자신이 보낸 예비 요청과 서버가 응답해준 정책을 비교해 해당 요청이 안전한지 확인 후, 서버에 본 요청을 다시 보냄
4. 서버가 본 요청에 대해 응답하면 최종적으로 이 응답 데이터를 자바 스크립트로 넘겨줌
  • 단순 요청 (Simple Request) 예비 요청을 생략하고 바로 서버에 본 요청을 한 후, 서버가 이에 대한 응답 헤더에 Access-Control-Allow-Origin을 보내주면 브라우저가 CORS 정책 위반 여부를 검사하는 방식이다. 단순 요청은 GET, POST와 같이 서버 자체 데이터가 변경 될 일이 적은 요청에서 작동된다. 하지만 POST의 경우, 서버 데이터를 변경할 수 있기에 SOP만 믿을 것이 아니라 따로 서버 프로그래밍을 해야 한다. 단순 요청(예비 요청 생략)을 할 수 있으려면 3가지 경우를 만족해야 한다.
    • 요청 Method는

      GET, HEAD, POST 중 하나여야 한다.

    • Content-Type은

      application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나여야 한다.

    • 유저 에이전트가 자동으로 설정한 헤더 외에, 수동으로 설정할 수 있는 헤더는 Fetch 명세에 “CORS-safelisted request-header”로 정의한 헤더만 사용할 수 있다.

      예시)

1. 클라이언트는 HTTP 요청 헤더에 Origin을 담아 서버로 전달
2. 서버는 응답 헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달
3. 클라이언트에서는 서버와 주고 받은 Origin과 Access-Control-Allow-Origin을 비교해 정책 위반 여부 확인
  • 인증된 요청 (Credentialed Request) 기존 예비 요청에서 보안을 더 강화하고 싶을 때 사용한다. 기본적으로 브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequest 객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다. 따라서, 요청에 인증과 관련된 정보를 담으려면 Credentials 옵션을 사용해야 한다. Axios를 사용할 때에는 withCredentials 옵션을 사용하면 된다. Credential Option
    • same-origin: (기본값) 같은 출처 간 요청에만 인증 정보를 담을 수 있음

    • include: 모든 요청에 인증 정보를 담을 수 있음

    • omit: 모든 요청에 인증 정보를 담지 않음

      예시)

      // Fetch Credential
      fetch('URL', {
      	method: 'PUT',
      	credentials: 'include'
      });
      
      // Axios withCredentials
      export function createInstance() {
      	const instance = axios.create({
      		withCredentials: true,
      	});
      	return setInterceptors(instance);
      };

      만약, 이러한 옵션을 사용해 리소스 요청에 인증 정보가 포함되면, 브라우저는 정책 위반 여부를 검사할 때, Access-Control-Allow-Origin뿐만 아니라 다른 조건 2가지을 추가해 검사한다.

    1. Access-Control-Allow-Origin에는 * 를 사용할 수 없으며 명시적인 URL이어야 함.
    2. 응답 헤더에는 반드시 Access-Control-Allow-Credentials: true 가 존재해야 함.
profile
기록만이 살 길 ... 말하는 감자애오

0개의 댓글