위 출처 내용을 정리한 글입니다.
보안 상의 이유로, 브라우저는 스크립트에서 시작한 교차 출처 HTTP 요청을 제한합니다.
다른 출처의 리소스를 불러오려면 그 출처(다른 출처)에서 올바른 CORS 헤더를 포함한 응답을 반환해야 합니다.
교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는
추가 HTTP 헤더를 사용하여,
한 출처에서 실행 중인 웹 애플리케이션이
다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록
브라우저에 알려주는 체제입니다.
웹 애플리케이션(브라우저)은 리소스가
자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다.
출처(Origin)
웹 콘텐츠의 출처(origin)는 접근할 때 사용하는 URL의 스킴(프로토콜), 호스트(도메인), 포트로 정의됩니다.
두 객체의 스킴, 호스트, 포트가 모두 일치하는 경우 같은 출처를 가졌다고 말합니다.
브라우저가 프론트엔드 JavaScript 코드에 대해
교차 출처(cross-origin)에 대한 응답에 접근하는 것을
차단하는지 여부를 결정하는 HTTP headers 전송으로 이루어진 시스템입니다.
기능적 개요
교차 출처 리소스 공유 표준은 웹 브라우저에서 해당 정보를 읽는 것이 허용된 출처를 서버에서 설명할 수 있는 새로운 HTTP 헤더를 추가함으로써 동작합니다.
추가적으로, 서버 데이터에 부수 효과(side effect)를 일으킬 수 있는
HTTP 요청 메서드(GET을 제외한 HTTP 메서드)에 대해,
CORS 명세는 브라우저가 요청을 OPTIONS 메서드로 "프리플라이트"(preflight, 사전 전달)하여 지원하는 메서드를 요청하고,
서버의 "허가"가 떨어지면 실제 요청을 보내도록 요구하고 있습니다.
또한 서버는 클라이언트에게 요청에 "인증정보"(쿠키, HTTP 인증)를 함께 보내야 한다고 알려줄 수도 있습니다.
CORS 실패는 오류의 원인이지만, 보안상의 이유로 JavaScript에서는 오류의 상세 정보에 접근할 수 없으며, 알 수 있는 것은 오류가 발생했다는 사실 뿐입니다. 정확히 어떤 것이 실패했는지 알아내려면 브라우저의 콘솔을 봐야 합니다.
사전요청이 안 일어나는 조건
단순 요청(Simple requests)이면 하지 않는다
단순 요청 조건
- 다음 중 하나의 메서드
- 유저 에이전트가 자동으로 설정 한 헤더 (예를들어, Connection, User-Agent (en-US), Fetch 명세에서 "forbidden header name"으로 정의한 헤더)외에, 수동으로 설정할 수 있는 헤더는 오직 Fetch 명세에서 "CORS-safelisted request-header"로 정의한 헤더 뿐입니다.
- 요청에 사용된 XMLHttpRequestUpload (en-US) 객체에는 이벤트 리스너가 등록되어 있지 않습니다. 이들은 XMLHttpRequest.upload 프로퍼티를 사용하여 접근합니다..
- 요청에 ReadableStream 객체가 사용되지 않습니다.
시나리오
간단한 시나리오(단순 요청)
https://foo.example 의 웹 컨텐츠가 https://bar.other 도메인의 컨텐츠를 호출
코드
아래는 간단 요청이다.
송수신
브라우저 => 서버
서버 => 브라우저
가장 간단한 시나리오는 Origin헤더(브라우저 요청)와 Access-Control-Allow-Origin 헤더(서버 응답)을 사용하는 것
일반 시나리오(프리플라이트 요청)
"preflighted" request는 위에서 논의한 "simple requests" 와는 달리,
먼저 OPTIONS 메서드를 통해 다른 도메인의 리소스로 HTTP 요청을 보내 실제 요청이 전송하기에 안전한지 확인합니다.
cross-origin 요청은 유저 데이터에 영향을 줄 수 있기 때문에 이와같이 미리 전송(preflighted)합니다.
예시
위와 같은 이유로 preflighted 요청이 발생한다.
전체 흐름
클라이언트와 서버간의 완전한 통신을 살펴보겠습니다.
첫 번째 통신은 preflight request/response입니다.
preflight request가 완료되면 실제 요청을 전송합니다.
자격 증명이 포함된 요청(Requests with credentials)
기본적으로 교차 출처 XMLHttpRequest 또는 Fetch 호출에서 브라우저는 자격 증명을 보내지 않습니다.
XMLHttpRequest 또는 Requset 생성자가 호출될 때 특정 플래그를 설정해야 합니다.
예시
https://foo.example 에서 로드된 콘텐츠 속,
https://bar.other 에 대한 Simple Request를 만들고 있음
기본적으로 호출은 쿠키 없이 이루어집니다.
이는 단순한 GET 요청이므로 preflighted 요청이 되지 않지만
브라우저는 Access-Control-Allow-Credentials: true 헤더가 없는 응답을 거부하고 호출하는 웹 콘텐츠에 응답을 제공하지 않습니다.
CORS 실행 전 사전 요청에서는 자격 증명이 포함되선 안됩니다.
Simple Request 이외 요청
사전 요청에 대한 응답은 Access-Control-Allow-Credentials: true를 지정하여 실제 요청이 자격 증명으로 이루어질 수 있음을 나타내야 합니다.
Credentialed requests에서 와일드카드
Access-Control-Allow-Origin
허용할 Origin 헤더 값
Access-Control-Allow-Headers
Access-Control-Allow-Methods
Access-Control-Expose-Headers
서버는 위 헤더를 반환할 때 '*' 와일드 카드를 반환해서는 안됩니다.
브라우저는 위 응답 헤더에 와일드 카드가 하나라도 있으면, Response에 대한 접근을 거부하고, 콘솔에 에러를 출력합니다.
위 헤더에 와일드 카드가 포함되지 않고, 자격 증명(보통 쿠키)이 있으면, 브라우저는 Response에 접근을 허용합니다.
쿠키 정책
CORS 응답에 설정된 쿠키에는 타사 쿠키 정책이 적용됩니다.
이유
위 예제에서 CORS 요청 페이지는 foo.example 에서 유래됐지만,
CORS 응답에 설정된 쿠키는 bar.other 서버에 의해 전송됐기 때문입니다.
브라우저가 모든 타사 쿠키를 거부하도록 구성된 경우 저장되지 않습니다.
이 경우, 자격 증명 수행을 못하게 됩니다.
쿠키 정책 관련 헤더 SameSite
CORS 사양에 정의된 액세스 제어 헤더
Response
Access-Control-Allow-Origin
허용할 origin
Access-Control-Expose-Headers
브라우저가 액세스할 수 있는 허용 목록에 헤더
Access-Control-Max-Age
preflight request 결과를 캐시할 수 있는 기간
Access-Control-Allow-Credentials
단순 요청인 경우, 이 값이 true여야만 브라우저가 응답에 접근합니다
이외 사전 요청의 경우, 이 값이 true이면 자격증명요청을 허용한다는 뜻입니다.
Access-Control-Allow-Methods
허용할 HTTP 메서드(예, POST, GET, DELETE…)
Access-Control-Allow-Headers
Preflight Request 이 후 실제 Request에 사용할 수 있는 헤더
Request
Origin
요청이 시작된 서버 URL
경로 정보는 포함되지 않고 서버 이름만 포함
Access-Control-Request-Method
실제 요청이 이루어질 때 어떤 HTTP 메서드가 사용될지 서버에 알리기 위해 Preflight Request에 사용
Access-Control-Request-Headers
실제 요청이 이루어질 때 어떤 HTTP 헤더가 사용될 것인지 서버에 알리기 위해 Preflight Request에 사용
이 브라우저 측 헤더는 서버 측 Access-Control-Allow-Headers 헤더에 의해 응답됩니다.
'네트워크, 보안' 카테고리의 다른 글
모든 개발자를 위한 HTTP 웹 기본 지식 요약 - 3 (0) | 2023.02.24 |
---|---|
모든 개발자를 위한 HTTP 웹 기본 지식 요약 - 2 (0) | 2023.02.23 |
모든 개발자를 위한 HTTP 웹 기본 지식 요약 - 1 (0) | 2023.02.21 |