교차 출처 리소스 공유 ( CORS )

Cross- Origin Resource Sharing

HTTP 헤더를 사용하여, ‘ 한 출처 (SOP)’에서 실행 중인 웹 어플리케이션이 다른 출처의 선택한 자원에 ‘접근할 수 있는 권한’을 부여하도록 ‘브라우저’에 알리는 체제

웹 어플리케이션은 리소스가 자신의 출처 (도메인 , 프로토콜 , 포트 )와 다를 때 출처 HTTP 요청(CORS)을 실행

교차 출처 요처의 예시 :  https://domain-a.com의 프론트 엔드 JavaScript 코드가 XMLHttpRequest를 사용하여 https://domain-b.com/data.json을 요청하는 경우.

[이미지 출처 : MDN]

보안상의 이유로, 브라우저는 스크립트에서 시작한 교차 출처 HTTP 요청을 제한 XMLHttpRequest와 Fetch API는 동일 출처 정책( SOP )을 따릅니다. 이 API를 사용하는 웹 애플리케이션은 자신의 출처와 동일한 리소스만(SOR) 불러올 수 있으며,
다른 출처의 리소스(CORS)를 불러오려면 그 출처에서 올바른 CORS 헤더를 포함한 응답을 반환해야한다.

기본적으로 CORS를 지원 하는 요소들

[<script> <img> <video> <link>]

CORS를 필요로 하는 요소들 ( 기본적으로 SOP 정책을 따르는 요소들 )

출처 ( Origin )

  1. Origin : Protocol + Host + Port 출처라는 것은 Protocol , Host, Port 모두를 합친 URL을 의미

SOP (Same Origin Policy) 동일 출처 정책

“ 동일한 출처(origin)에서만 리소스를 공유할 수 있다. “

SOP가 필요한 이유 해커가 악의적으로 홈페이지에 접속하는 상황을 막기 위한 기본적 조치 출처: inpa Dev

동일한 출처란 URL 구성요소 중 Protocol(Schema), Host, Port 가 동일하면 같은 출처로 인지 출처: inpa Dev

출처에 대한 비교는 서버가 아닌 브라우저에서 실행한다. 출처: inpa Dev 응답 데이터는 요청 받은데로 정상적으로 전달하지만 브라우져에서 해당 reponse를 동일출처정책으로 차단한다.

CORS는 SOP로 막혀 받아올 수 없는 response 를 받을 수 있는 방법 중 하나 SOP 정책을 위반 하더라도 CORS 정책을 따르면 받아 다른 출처의 리소스 허용이 가능하다

브라우저 CORS 기본 동작

  1. Client에서 HTTP 요청의 header에 Origin을 담아 전달
    1. Origin Field에 출처를 담아 보낸다.
  2. Server는 response header에 Access-Control-Allow-Origin을 담아 클라이어트로 전달
    1. ’ 이 리소스를 접근하는 것이 허용된 출처’
  3. 클라이언트에서 자신이 보냈던 request Origin 과 === 서버가 보내준 Access-Control-Allow-Origin 을 비교한다.
    1. 유효하지 않다면 ( !== ) CORS ERROR!
    2. 유효하다면 ( === ) 다른출처의 리소스를 가져온다.

*서버에서 해야할 일은 이 response “Access-Control-Allow-Origin” 을 헤더에 허용할 출처를 기재해서 클라이언트에 응답을 하면 된다. *


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

예비요청 (Preflight Request)

브라우저는 Request를 진행 할때 예비요청 (Preflight)를 먼저 보내 서버와 통신이 잘 되는지 확인 후 본 요청을 보내게 된다. ( 브라우저가 안전한지 미리 확인하는 과정 ) 특별한 점은 HTTP Method가 GET / POST 등의 방식이 아닌 “OPTION” 이라는 요청을 사용

출처: inpa Dev

출처: inpa Dev

단순 요청 (Simple Request)

예비요청을 생략하고 본 요청을 보내는 방식 출처: inpa Dev

단순요청은 특별한 경우를 만족해야만 실행 가능한 방법이다.

  1. 요청의 method가 ‘GET, HEAD, POST’ 중 하나여야 한다.
  2. Accept, Accept-Language, Content-Language,  Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width  헤더일 경우에만 적용된다
  3. Content-Type 헤더가 application / x-www-form-urlencoded,  multipart / form-data, text / plain중 하나여야한다. 아닐 경우 예비 요청으로 동작된다.

    위 조건을 모두 만족되어 단순 요청이 일어나는 상황은 드물다고 보면 된다. 왜냐하면 대부분 HTTP API 요청은 text / xml 이나 application / json 으로 통신하기 때문에 3번째 Content-Type이 위반되기 때문이다.

따라서 대부분의 API 요청은 그냥 예비 요청(preflight)으로 이루어진다 라고 이해하면 된다.

인증된 요청 (Credentialed Request)

인증된 요청은 클라이언트에서 서버에게 자격 인증 정보(Credential)를 실어 요청할때 사용되는 요청이다.

자격 인증 정보란 세션 ID가 저장되어있는 쿠키(Cookie) 혹은 Authorization 헤더에 설정하는  토큰 값 등을 일컫는다.

즉, 클라이언트에서 일반적인 JSON 데이터 외에도 쿠키 같은 인증 정보를 포함해서 다른 출처의 서버로 전달할때 CORS의 세가지 요청중 하나인 인증된 요청으로 동작된다는 말이며, 이는 기존의 단순 요청이나 예비 요청과는 다른 인증 형태로 통신하게 된다.

1. 클라이언트에서 인증 정보를 보내도록 설정하기

Default : 별도의 옵션 없이 브라우저의 쿠키와 같은 인증과 관련된 데이터를 함부로 Request 데이터에 담지 않음 요청에 인중과 관련된 정보를 담을 수 있게 해주는 옵션 ‘credentiais’ 옵션

3가지 옵션 사용 가능

  1. same-origin : 같은 출처 간 요청에만 인증 정보를 담을 수 있다.
  2. include : 모든 요청에 인증 정보를 담을 수 있다.
  3. omit : 모든 요청에 인증 정보를 담지 않는다.

서버에 인증 요청을 담는 방법

  1. fetch 메서드 이용
  2. axios
  3. jQuery

1. fetch 메서드 사용

// fetch 메서드
fetch("https://example.com:1234/users/login", {
	method: "POST",
	credentials: "include", 
	// 클라이언트와 서버가 통신할때 쿠키와 같은 인증 정보 값을 공유하겠다는 설정
    body: JSON.stringify({
        userId: 1,
    }),
})
  1. axios 라이브러리
    // axios 라이브러리
    axios.post('https://example.com:1234/users/login', { 
     profile: { username: username, password: password } 
    }, { 
     withCredentials: true 
     // 클라이언트와 서버가 통신할때 쿠키와 같은 인증 정보 값을 공유하겠다는 설정
    })
    
  2. jQuery
    // 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));
     }
    });
    

2. 서버에서 인증된 요청에 대한 헤더 설정

  1. Access-Control-Allow-Credentials : true
  2. Access-Control-Allow-Origin 의 값에 와일드카드 문자(“*“)는 사용할 수 없다.
  3. Access-Control-Allow-Methods 의 값에 와일드카드 문자(“*“)는 사용할 수 없다.
  4. Access-Control-Allow-Headers 의 값에 와일드카드 문자(“*“)는 사용할 수 없다.

출처: inpa Dev

CORS 튜토리얼 참고