교차 출처 리소스 공유(Cross-Origin Resource Sharing,CORS)에 대해 알아보자

이지훈·2021년 1월 10일
0

CORS에 대해 알아보면서 MDNjavascript.info의 설명을 많이 참고했습니다

CORS란?

MDN의 CORS에 대한 설명을 보면 다음과 같습니다

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다.

이 설명만 들었을 때 어떠한 자원과 출처에 대한 정책이라는 것은 알 수 있지만, 여전히 이해하기 어려웠습니다.

보다 나은 이해와 왜 이러한 정책을 사용하게 되었는지 알기 위해서는 동일 출처 정책(SOP)라는걸 알 필요가 있습니다.

SOP(Same-Origin Policy)

이번에도 MDN에서 살펴본 동일 출처 정책에 대한 설명은 다음과 같습니다.

동일 출처 정책(same-origin policy)은 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식입니다. 동일 출처 정책은 잠재적으로 해로울 수 있는 문서를 분리함으로써 공격받을 수 있는 경로를 줄여줍니다.

즉, 외부로부터의 공격 또는 그런 잠재적으로 위험이 있는 자원을 미리 차단하기 위해 다른 출처에서 가져온 자원을 사용하지 못하게 제한합니다.

SOP 우회

SOP 규약은 인터넷 보안을 위한 근본 정책이었고, 이 당시의 자바스크립트는 아직 네트워크 요청을 보낼 만한 메서드를 지원하지 않았다고 합니다.

하지만 시간이 흐르며 점점 많은 웹 개발자들이 더 강력하고 다양한 기능을 원하기 시작했습니다. 결국 트릭을 사용하여 SOP 제약을 회피하기 위한 시도가 있었는데 <form>을 사용하여 form 안에 iframe을 넣어서 다른 사이트에 요청을 보내거나, script 태그의 src속성값을 사용하는 것 등이 있었습니다.

이후 긴 논의 끝에 크로스 오리진 요청을 허용하며, 명시적으로 크로스 오리진 요청을 허가하는지 여부를 알려주는 특별한 헤더를 전송받았을 때만 가능하도록 제약을 걸어두게 되었습니다.

CORS 시나리오

CORS을 위해서는 CORS를 허용한다는 내용의 특별한 헤더를 응답에 포함시켜야 합니다. 그렇지 않다면, 브라우저는 이것이 올바르지 못한 요청이라고 파악해서 요청이 실패하게 됩니다. 크로스 오리진 요청은 Simple과 non-simple로 나뉩니다. non-simple요청에서는 preflight요청이 본 요청 전에 전송되며, 추가로 자격 증명이 있는 요청도 있습니다.

simple 요청

simple 요청은 다음의 요구조건을 모두 충족해야 합니다. 이 조건을 충족하지 않는 요청은 모두 'non-simple 요청'으로 취급됩니다.

메서드 :
GET
HEAD
POST

헤더 :
Accept
Accept-Language
Content-Language
Content-Type ( 값이 application/x-www-form-urlencoded이나 multipart/form-data, text/plain여야한다)

Simple 요청의 간단한 절차는 다음과 같습니다. javascript.info의 내용입니다.


→ 오리진 정보가 담긴 Origin 헤더와 함께 브라우저가 요청을 보냅니다.
← 자격 증명이 없는 요청의 경우(기본), 서버는 아래와 같은 응답을 보냅니다.
Origin 값과 동일하거나 *인 Access-Control-Allow-Origin
← 자격 증명이 있는 요청의 경우 서버는 아래와 같은 응답을 보냅니다.
Origin 값과 동일한 Access-Control-Allow-Origin
값이 true인 Access-Control-Allow-Credentials

non-simple 요청

과거엔 메소드가 GET과 POST밖에 없었지만, 요즘엔 PATCH, DELETE 등의 HTTP 메서드를 사용 할 수 있습니다. 하지만 오래된 웹서버들은 요즘의 PATCH, DELETE등의 메서드를 다룰 수 없고 이를 브라우저가 보낸 요청이 아니라고 파악합니다.

이런 상황을 해결하고자 브라우저는 바로 본 요청을 보내기 전에 사전 요청(preflight 요청)을 먼저 보냅니다.


preflight 요청은 OPTIONS 메서드를 사용합니다. 헤더엔 아래 두 항목이 들어가고, 본문은 비어있습니다.
Access-Control-Request-Method 헤더 – non-simple 요청에서 사용하는 메서드 정보가 담겨있습니다.
Access-Control-Request-Headers 헤더 – non-simple 요청에서 사용하는 헤더 정보가 담겨있습니다. 각 항목은 쉼표로 구분됩니다.

non-simple 요청을 받아들이기로 동의한 경우, 서버는 본문이 비어있고 상태 코드가 200인 응답을 다음과 같은 헤더와 함께 브라우저로 보냅니다.
Access-Control-Allow-Methods – 허용된 메서드 정보가 담겨있습니다.
Access-Control-Allow-Headers – 허용된 헤더 목록이 담겨있습니다.
Access-Control-Max-Age – 퍼미션 체크 여부를 몇 초간 캐싱해 놓을지를 명시합니다. 이렇게 퍼미션 정보를 캐싱해 놓으면 브라우저는 일정 기간 동안 preflight 요청을 생략하고 non-simple 요청을 보낼 수 있습니다.

자격 증명(credential)

자격 증명은 조금 더 보안이 강한 옵션이라고 생각됩니다.

HTTP 요청은 보통 쿠키가 함께 전송되는데, 자바스크립트 메서드를 사용해 만든 크로스오리진 요청은 기본적으로 쿠키가 함께 전송되지 않습니다.

이는 자격 증명과 함께 전송되는 요청은 영향력이 강하기 때문입니다. 자바스크립트가 사용자를 대신해 민감한 정보에 쉽게 접근하지 않도록 기본적으로는 자격증명이 아니게 한 것이죠. 그렇지만 서버에서 이를 허용하고 싶다면, 자격증명이 담긴 헤더를 명시적으로 허용해줘야 합니다.

fetch 메서드를 예로 자격 증명 정보를 함께 전송한다면 credentials: "include" 옵션을 추가해줘야 합니다. (same-origin : 같은 출처간 요청에만 인증정보를 담는다, include : 모든 요청에 인증정보를 담는다, omit : 모든 요청에 인증정보를 담지 않는다)

서버 측에서도 자격증명정보가 담긴 요청을 받아들이기로 동의했다면, 응답에 Access-Control-Allow-Origin 헤더와 함께 Access-Control-Allow-Credentials: true 헤더를 추가해서 보냅니다.
예를 들면 이런 헤더가 됩니다.

200 OK
Access-Control-Allow-Origin: https://javascript.info
Access-Control-Allow-Credentials: true

이러한 자격증명이 함께 전송되는 요청에서는 Access-Control-Allow-Origin에 명확히 출처를 명시해야 하며 *를 쓸 수 없습니다. 이는 어떤 출처에서 요청이 왔는지에 대해 서버가 신뢰할 수 있도록 하기 위한 제약입니다.

더 공부해봐야할 것

HTTP와 같은 기본적인 공부가 더 필요한것같네요. 또 문서들을 찾아다니다 보니 자주 보이지만 조금은 어려운 것들이 있었습니다. 하나씩 찾아봐야겠네요

  • XHR
  • Spring security
  • HTTP / HTTPs
  • HTTP Header
profile
안녕하세요! 대학교 졸업한 이지훈입니다.

0개의 댓글