CORS가 어려운 이유 - 4. Origin과 Same-origin Policy
- 카테고리 없음
- 2024. 11. 13.
CORS 에러는 웹 개발에 입문하는 많은 사람들에게 좌절을 안겨주는 유명한 오류입니다. CORS 에러는 웹을 이루는 다양한 요소를 먼저 이해하고 나면, 어렵지 않게 이해할 수 있습니다. 하지만 그 요소가 무척이나 많은데요. 필요한 개념들을 다양한 실습과 함께 모두 뜯어보고 이해해봅시다. [CORS 가 어려운 이유]는 시리즈로 구성됩니다. 아래 목차를 확인해주세요.
CORS 가 어려운 이유
- CORS 에러 마주하기
- HTTP 요청과 응답의 구조
- CORS 에러 발생 주체와 시점
- Origin과 Same-origin Policy (현재 글)
✅ 출처(origin)의 의미를 알고싶다면 한 번 읽어보세요.
✅ 교차출처(Cross-origin)의 의미와 동일출처(Same-origin)의 의미를 알고싶다면 한 번읽어보세요.
✅ 웹브라우저의 동일출처정책(Same-origin Policy)가 무엇인지 알고 싶다면 한 번 읽어보세요.
✅ 웹에서 출처(origin)이 실질적으로 어떤 방식으로 활용되는지 확인해보고 싶다면 한 번 읽어보세요.
Origin
origin 은 '출처'로 번역합니다. (이 글에서는 모두 '출처(origin)'로 표기합니다.) 출처는 3가지 요소로 구성됩니다.
- scheme(protocol)
- domain(hostname)
- port
웹브라우저에서의 출처의 의미
먼저, HTTP 통신에서 일반적으로 쓰이는 'origin' 이라는 용어와 'Origin HTTP 요청헤더'를 서로 분리하여 이해할 필요가 있습니다. 출처가 scheme, domain, port 로 구성된다는 것을 아는 것 이상으로 Origin이 출처의 의미를 갖는 요청헤더라는 사실을 이해하는 것이 중요합니다.
The HTTP Origin request header indicates the origin (scheme, hostname, and port) that caused the request. For example, if a user agent needs to request resources included in a page, or fetched by scripts that it executes, then the origin of the page may be included in the request.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
origin 용어 자체는 '자원의 출처'를 가리킵니다. "HTTP Origin 요청헤더는 요청을 일으킨 출처를 가리킴"이라고 명시되어 있습니다. 사용자가 보고 있는 웹페이지에서 포함된 자원(img, video 태그 등)을 요청할 때, 또는 스크립트에 의해 fetch 될 때 출처는 HTTP 요청에 포함될 수 있습니다.
동일출처정책: Same-origin Policy
https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
'동일출처정책'으로 번역합니다. SOP 는 브라우저단에서 로드된 문서(html)나 스크립트(script)에서 다른 출처의 자원과 어떻게 상호작용할지 정해놓은 정책입니다. 같은 출처의 자원이라면 허용하고, 다른 출처의 자원이라면 정해진 규칙에 따라 허용하기도 하고 제한하기도 합니다. 교차출처간의 자원 활용에 대한 규칙이 위 링크(MDN)의 Cross-origin network access 에 기술되어 있습니다.
Cross-origin network access
교차출처자원을 활용하는 방법은 크게 3가지 교차출처 쓰기(write), 교차출처 삽입(embedding), 교차출처 읽기(read)가 있습니다.
교차출처 쓰기
교차출처 쓰기는 보통 허용됩니다. 예시는 다음과 같습니다.
- 폼제출
- preflight(OPTIONS) 가 필수인 HTTP 요청(POST, PUT 등)
- link(a태그)
- redirect(스크립트상에서window.location 제어) 등
교차출처 삽입
교차출처 삽입은 보통 허용됩니다. 예시는 다음과 같습니다.
- 문서에 포함된 img 태그로 교차출처의 자원 삽입
(아래 "Cross-origin 시나리오1" 섹션에서 다룹니다.) - 문서에 포함된 video 태그로 교차출처의 자원 삽입 등
교차출처 읽기
교차출처 읽기는 보통 제한됩니다. 예시는 다음과 같습니다.
- <script> 내부 fetch() 함수 호출
(위 "Cross-origin 시나리오2" 섹션에서 다룹니다.) - <script> 내부 XMLHttpRequst 활용 호출 등
💡 웹브라우저에서 저장소(data storage)도 SOP 정책의 대상에 포함됩니다. 쿠키제어, Local Storage 등 모두 웹브라우저에 로드된 페이지가 제어하기 때문에 저장소의 데이터 출처를 갖습니다. 저장소의 데이터는 출처간에 공유될 수 없습니다.
https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#cross-origin_data_storage_access
Cross-origin & Same-origin
두 개의 출처가 다르다는 것은 출처의 구성요소가 하나 이상 다르다는 것을 의미합니다.
- https://google.com 과 https://youtube.com 는 서로 다른 출처입니다.
- https://google.com 과 http://google.com 은 서로 다른 출처입니다. (프로토콜이 다릅니다.)
- https://angel.com:1234 과 http://angel.com:4321 은 서로 다른 출처입니다. (포트가 다릅니다.)
이렇게 서로 다른 출처간에 상호작용이 일어났다면 이를 '교차출처(cross-origin)'간 상호작용이라고 표현합니다.
상호작용이 일어난 두 개의 출처가 서로 같다면 이를 '동일출처(same-origin)'간 상호작용이라고 표현합니다.
Same-origin 시나리오
- 브라우저에 주소 입력: devil.com
- devil.com 이 응답: 본문의 html 에 <img src="/face.png"/> 포함 - 삽입(embedding)방식
(Cross-origin network access 에서 삽입 방식에 해당) - 자원요청: 브라우저가 face.png 를 현재 페이지의 출처를 host로 하여 자원 요청 - 동일출처간 상호작용
Cross-origin 시나리오1
- 브라우저에 주소 입력: devil.com
- devil.com 이 응답: 본문의 html에 <img src="angel.com/logo.png"/> 포함
(Cross-origin network access 에서 브라우저에 의해 상호작용이 허용되는 삽입 방식에 해당) - 자원요청: 브라우저가 angel.com 에 logo.png 를 요청하면서 Origin 요청헤더를 devil.com 으로 지정 - 교차출처간 상호작용
상호작용이란 브라우저에서 발생합니다. 웹페이지에 로드된 문서(html & script)에서 대상 자원을 활용하는 것을 말합니다. 가령 브라우저에서 devil.com 에 접속했을 때 응답된 html 문서에 <img src="angel.com/logo.png"> 태그가 포함되어 있다면 브라우저는 angel.com/logo.png 자원으로의 접근을 시도합니다. 브라우저의 주소창에 의해 로드된 devil.com 의 자원이 angel.com 의 자원을 요청하면서 둘 사이에 상호작용이 일어났습니다! 이제야 비로소 angel.com 과 devil.com 은 서로 교차출처의 관계가 되었습니다.
💡 교차출처 관계이지만 <img> 태그로 자원을 '삽입' 하는 상호작용은 브라우저가 허용하는 상호작용입니다. 즉, Origin 요청헤더가 포함되지 않고 src 로 요청합니다.
만약 클라이언트 입장에서 CORS 를 활성화하고 싶다면 태그에 crossorigin 속성을 부여해줍니다. 요청헤더에 Origin 이 포함되며, 서버의 CORS 응답헤더에 따라 브라우저는 CORS 에러를 발생시킵니다.
https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin
Cross-origin 시나리오2
- 브라우저에 주소 입력: devil.com
- devil.com 이 응답: 본문의 script에 `fetch('angel.com/weapons)` 포함
(Cross-origin network access 에서 브라우저에 의해 상호작용이 제한되는 읽기 방식에 해당) - 자원요청: 브라우저가 angel.com 에 /weapons 를 요청하면서 Origin 요청헤더를 devil.com 으로 지정 - 교차출처간 상호작용
devil.com 이 angel.com 에 /weapons 자원을 요구했습니다. 교차출처간 상호작용입니다. 브라우저는 교차출처에 대해 fetch 를 하는 경우 Origin 태그를 포함합니다.
💡 요즘 웹 개발은 보통 프론트엔드와 백엔드로 구분하여 이루어집니다. 그렇기 때문에 프론트엔드 서버의 출처와 백엔드 서버의 출처가 다른 경우가 일반적입니다. 프론트엔드 소스코드에서 백엔드 API 를 호출할 때 교차출처 읽기 방식의 상호작용이 일어날 수 있습니다. 이는 웹브라우저의 SOP 정책에 의해 제한되는 상황이기에 CORS 에러가 발생합니다. 이를 해결하는 방법에 대해서는 5장에서 알아봅니다.
Cross-origin 시나리오3
angel.com 에서 god.com/bible 조회하기
: 교차출처간 fetch 가 허용되는 시나리오를 구성해봅니다. 다음 글에서 다룹니다. (미리 스포하자면, CORS 는 서버에서 응답헤더를 제어하여 교차출처간 상호작용을 제어하는 방법론입니다.)