본문 바로가기
Network/HTTP

CORS

by joooing 2021. 2. 7.
반응형

등장배경


옛날에는 서버에서 내려받은 client로 통신을 했기 때문에, 출처가 같아 의심의 여지가 없었다. 하지만 최근의 웹들을 생각해보면 여기저기서 받은 여러 리소스들을 한 클라이언트가 활용해야할 필요가 생겼다. 이제는 출처가 다르기 때문에 한번쯤은 의심을 해봐야 할 상황이 된 것이다. 이런 배경에서 다른 출처의 리소스에 접근 가능한 권한을 부여하는 정책이 생겨나게 되었다. 이 중 하나가 바로 CORS이다.

(왼) 옛날 (오) 현재

 

CORS


CORS는 새로운 HTTP 헤더를 추가함으로써 웹 어플리케이션이 출처가 다른 리소스에 접근 권한을 부여하도록 브라우저에게 알려주는 체제이다.

 

여기서 다른 출처프로토콜, 호스트, 포트가 다른 경우를 의미한다. 아래처럼 URL은 스키마(프로토콜), 호스트, 포트번호, 경로, 쿼리문 등으로 구성되어있는데 이 중 스키마 && 호스트 && 포트만 동일하면 출처가 같다고 판단하는 것이다. 참고로 포트번호는 보통 생략되는데, 명시된 경우에만 일치하면 된다.

 

보안상의 이유로 브라우저에서의 교차 출처 요청은 서버가 허용(Allow)한 범위 내에서만 가능하도록 제한되어있다. 아래처럼 origin, method, headers 등의 옵션을 지정할 수 있다.

 

'access-control-allow-origin' : '*'  // 도메인
'access-control-allow-methods' : 'GET, OPTIONS'  // 메서드
'access-control-allow-headers' : 'Content-type'  // 헤더
'access-control-allow-origin' : 10   // preflight 가능 시간

 

 

CORS의 동작


Request (요청) : HTTP 프로토콜로 헤더 Origin의 값으로 출처를 담아 보냄

Response (응답) : 헤더 Access-Control-Allow-Origin 값으로 허용된 출처를 보냄

Browser (브라우저) : Origin 값과 Access-Control-Allow-Origin 값을 비교해 유효성을 판단함

 

 

CORS의 요청방법 세가지


1. Preflight Request

Preflight 요청 상황에서 브라우저는 요청을 예비 요청 + 본 요청으로 나눠서 보낸다. 주의할 점은 예비 요청을 할 때 OPTIONS 메서드를 반드시 사용해야한다는 것이다. 그림으로한번 살펴보자.

예비 요청 부분에서는 앞서 말했듯 OPTIONS 메서드를 사용해야 하고, 헤더의 Origin 값으로는 출처에 대한 정보가 들어가야 한다. 이 때 헤더에는 Origin뿐만 아니라 본요청에 대한 다른 정보들도 포함될 수 있다.

 

Preflight Request에서 주의해야 할 부분은 CORS위반 여부를 판단하는 시점이다. CORS에 대한 판단과 응답 파기 여부는 브라우저가 결정한다. 따라서 CORS 정책을 위반하더라도 응답 헤더에 200 OK가 뜨기는 한다. 정책 여부 위반 판단은 상태코드가 아닌 Access-Control-Allow-Origin 값이 존재하는지 여부로 판단해야 한다! 좀 더 생각해보면 요청이 실패하더라도(= 200 OK가 아니어도) Access-Control-Allow-Origin 값이 존재하면 CORS위반은 아니라는 뜻이기도 하다.

 

// 헤더를 담아 fetch메서드를 사용하는 과정
let headers = new Headers({
	'Content-Type' : 'text/plain'
});
fetch('http://jooing.com', {headers});

 

2. Simple Request

Simple Request는 예비 요청 없이 본 요청만 바로 보내는 요청이다.

 

Simple Request 조건

Simple Request가 되기 위해서는 아래와 같은 조건들을 모두 만족시켜야만 한다. 이 중 하나라도 만족하지 않으면 Preflight 요청을 날려버린다.

 

조건 1. 메서드

: GET, POST, HEAD

조건 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

 

3. Credentialed Request

Credentialed Request는 인증된 요청을 사용하는 요청 방식이다. 보안 강화 목적으로 사용하는 방식이다. XMLHttpRequest나 fetchAPI같은 브라우저가 기본 제공하는 비동기 리소스 요청들은 옵션을 따로 지정해주지 않으면 헤더에다가 쿠키나 인증관련 정보를 함부로 담지 않는다. 따라서 쿠키, 인증 정보를 담고 싶다면 credentials 라는 옵션에 값을 지정해 주어야 한다.

 

credentials 옵션의 값으로는 아래와 같이 세 가지를 지정할 수 있다.

 

1) same-origin : 출처가 같은 요청에만 인증정보를 담을 수 있음 (크롬 default)

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

3) omit : 모든 요청에 인증정보를 담을 수 있음

 

이 중 include를 값으로 지정하려면 아래의 두 가지 조건을 더 만족해야 한다.

1) Access-Control-Allow-Origin 값이 * 이어서는 안된다.

2) 응답 헤더에 반드시 Access-Control-Allow-Credentials:true 가 존재해야 한다.

 

 

 

CORS정책을 위반한 경우, 가장 일반적으로 Access-Control-Allow-Origin값을 세팅함으로써 해결이 가능하다. 값으로 * 를 지정해주면, 모든 출처를 허용하기 때문에 약간 위험할 수 있다. 따라서 정확한 출처(url)를 명시해주는것이 좀더 안전하게 해결하는 방법이다.

 

CORS 정책 위반으로 인한 에러때문에 귀찮을 수도 있지만, 이런 정책 덕분에 여기저기서 리소스들을 가져올 때 안전성을 보장받을 수 있는 것이니 너무 귀찮아하지 말자!

 

 

반응형

댓글