웹 개발 필수 개념: SOP, CORS, CORP의 관계 정리
작성일:
현대 웹 환경에서는 Cross-Origin 리소스 접근을 관리하기 위한 복잡한 보안 메커니즘이 존재합니다. 그 중심에는 SOP, CORS, CORP 세 가지 정책이 있습니다.
특히 CDN(Content Delivery Network)을 사용하는 경우, 이 정책들을 정확히 이해하지 못하면 예측하지 못한 로드 오류(CORS/CORP 에러)에 직면하게 됩니다.
1. 웹 보안의 근간: 출처(Origin)와 SOP
웹 보안의 모든 논의는 출처(Origin)의 개념에서 시작됩니다.
1-1. 출처(Origin)의 정의
두 URL이 동일한 출처가 되기 위해서는 다음 세 가지 요소가 모두 일치해야 합니다.
- 프로토콜 (Scheme):
http또는https - 호스트 (Host): 도메인 이름 (예:
example.com) - 포트 (Port): 포트 번호 (생략 시 기본값 80 또는 443)
1-2. 동일 출처 정책 (SOP: Same-Origin Policy)
SOP는 웹 브라우저의 가장 기본적인 보안 정책입니다.
- 목표: 한 출처에서 로드된 문서나 스크립트가 다른 출처의 리소스에 임의로 접근하여 정보를 읽는 것을 차단합니다. 이를 통해 CSRF(교차 사이트 요청 위조), 정보 유출 등의 악성 행위를 방지합니다.
- 기본 원칙: 기본적으로 동일 출처 간의 상호작용만 허용합니다.
2. SOP의 예외: CORS (Cross-Origin Resource Sharing)
CORS는 SOP가 엄격하게 제한하는 스크립트 기반의 교차 출처 데이터 통신을 안전하게 허용하기 위한 표준 메커니즘입니다.
2-1. CORS의 역할과 작동 방식
| 구분 | CORS (Cross-Origin Resource Sharing) |
|---|---|
| 목표 | 스크립트 기반의 교차 출처 데이터 읽기 허용 (SOP의 예외 부여). |
| 대상 | Fetch API, XMLHttpRequest 등을 사용한 동적 API 요청. |
| 작동 주체 | 서버가 정책을 설정하고, 브라우저가 이를 검증 및 실행. |
| 핵심 헤더 | Access-Control-Allow-Origin: <허용 출처> |
작동 흐름:
- 브라우저: 교차 출처 요청 시
Origin헤더에 자신의 출처 정보를 담아 서버에 보냅니다. (복잡한 요청의 경우 Preflight 요청을 먼저 보냅니다.) - 서버:
Origin헤더를 확인하고, 허용 목록에 있으면 응답 헤더에Access-Control-Allow-Origin을 포함하여 브라우저에 보냅니다. - 브라우저: 응답 헤더를 검증합니다. 허용되지 않은 출처라면, 서버가 응답을 성공적으로 보냈더라도 브라우저가 스크립트가 해당 응답 데이터에 접근하는 것을 차단하고 콘솔에 CORS 에러를 띄웁니다.
📝 서버 측 필수 응답 헤더 (CORS)
서버는 상황에 따라 다음 헤더들을 적절히 조합하여 내려주어야 합니다.
| 헤더 이름 | 설명 | 예시 |
|---|---|---|
Access-Control-Allow-Origin |
접근을 허용할 출처(Origin)를 지정합니다. *은 인증이 필요 없는 경우에만 사용 가능합니다. |
https://example.com |
Access-Control-Allow-Methods |
리소스 접근 시 허용할 HTTP 메서드 목록입니다. (Preflight 응답 시 사용) | GET, POST, OPTIONS |
Access-Control-Allow-Headers |
실제 요청에서 사용할 수 있는 커스텀 헤더 목록입니다. | Content-Type, Authorization |
Access-Control-Allow-Credentials |
쿠키(Cookie)나 인증 헤더를 포함한 요청을 허용할지 여부입니다. true일 때만 자격 증명 포함 요청이 성공합니다. |
true |
Access-Control-Expose-Headers |
브라우저 스크립트에서 접근할 수 있는 응답 헤더 화이트리스트입니다. 기본적으로 6개 헤더 외에는 읽을 수 없습니다. | X-My-Custom-Header |
Access-Control-Max-Age |
Preflight 요청의 결과를 캐시할 시간(초)입니다. 캐시된 시간 동안은 다시 Preflight를 보내지 않습니다. | 3600 |
2-2. 상세 프로세스: 클라이언트와 서버의 대화
CORS는 마치 “입국 심사”와 유사한 과정을 거칩니다.
1) Simple Request (단순 요청)
안전한 메서드(GET, HEAD, POST)와 안전한 헤더(Accept, User-Agent 등)만 사용하는 경우입니다.
[Browser] [Server]
| |
| --- GET /api/data -----------------------> |
| (Origin: https://my-app.com) |
| |
| <---------------------- 200 OK ----------- |
| (Access-Control-Allow-Origin: |
| https://my-app.com) |
| |
V V
[JS App]
(성공!) 브라우저가 헤더 확인 후
데이터 전달
2) Preflight Request (예비 요청)
PUT, DELETE 등 데이터를 변경할 수 있는 요청이나 커스텀 헤더(Authorization 등)를 사용할 때 발생합니다. 브라우저는 본 요청을 보내기 전에 “간 보기(Preflight)” 요청을 먼저 보냅니다.
[Browser] [Server]
| |
| --- OPTIONS /api/update -----------------> |
| (Origin: ..., Method: PUT) |
| "나 좀 이따 PUT 보낼 건데 괜찮?" |
| |
| <----------------- 204 No Content -------- |
| (Allow-Methods: PUT) |
| "ㅇㅇ PUT 써도 됨" |
| |
| --- PUT /api/update ---------------------> |
| |
| <---------------------- 200 OK ----------- |
3. 새로운 방어막: CORP (Cross-Origin Resource Policy)
CORP는 CORS와 달리, 스크립트 기반 요청뿐만 아니라 HTML 태그를 통한 리소스 포함(Embedding)까지 제어하여, 민감한 리소스가 다른 출처의 메모리에 로드되는 것 자체를 방지하는 강력한 보안 정책입니다.
3-1. CORP의 역할 및 도입 배경
| 구분 | CORP (Cross-Origin Resource Policy) |
|---|---|
| 목표 | 리소스의 메모리 로드 자체를 차단하여 Spectre와 같은 사이드 채널 공격으로부터 정보를 보호. |
| 대상 | <script>, <img>, <link> 등 포함(Embedding) 가능한 모든 정적 리소스. |
| 작동 주체 | 서버가 정책을 설정하고, 브라우저가 이를 검증 및 실행. |
| 핵심 헤더 | Cross-Origin-Resource-Policy: <정책> |
📝 서버 측 필수 응답 헤더 (CORP)
CORP는 단 하나의 헤더로 제어되지만, 정책의 의미가 매우 중요합니다.
| 정책 값 (Value) | 설명 | 사용 예시 |
|---|---|---|
same-origin |
가장 강력한 제한. 동일한 출처(같은 Scheme, Host, Port)에서만 리소스를 로드할 수 있습니다. | 뱅킹 데이터, 개인정보 JSON |
same-site |
동일한 상위 도메인(Site) 내에서는 로드 허용. 서브도메인 간 공유가 필요할 때 사용합니다. | login.naver.com ↔ mail.naver.com |
cross-origin |
모든 출처 허용. CDN이나 공개 이미지처럼 어디서든 로드되어도 되는 리소스에 사용합니다. | CDN 호스팅 이미지, 공개 JS 라이브러리 |
3-2. CORP vs. CORS: 명확한 차이
| 구분 | CORP (Cross-Origin Resource Policy) | CORS (Cross-Origin Resource Sharing) |
|---|---|---|
| 제어 대상 | 리소스의 로드(Load) 및 포함(Embedding) 자체 | 스크립트의 응답 데이터 접근(Read) |
| 보안 목적 | 메모리 기반의 정보 유출 방지 | SOP의 제약을 해제하여 API 통신 허용 |
3-3. 상세 프로세스: 브라우저의 입국 심사
CORP는 서버가 브라우저에게 “나를 아무데나 태우지 마!”라고 신분증을 보여주는 것과 같습니다. 브라우저는 이 신분증을 보고 로드 여부를 결정합니다.
[Attacker.com] [Browser] [Bank Server]
| | |
| -- <img src> ---> | |
| | |
| | --- GET /user_info.png --> |
| | |
| | <------- 200 OK ---------- |
| | (CORP: same-origin) |
| | |
X | (차단!) |
(로드 실패) [메모리 파기]
"잠깐, 출처가 다른데
same-origin이네? 버렷!"
- Client (브라우저): 응답을 받았을 때,
Cross-Origin-Resource-Policy헤더를 확인합니다. 현재 페이지의 출처(Origin)와 리소스의 CORP 정책이 맞지 않으면, 응답 내용을 메모리에 올리지 않고 즉시 파기합니다. (렌더링 자체가 안 됨) - Server: 리소스가 어디서 로드되어도 되는지(
cross-origin), 같은 출처에서만 로드되어야 하는지(same-origin), 혹은 같은 사이트(same-site)인지 헤더로 명시합니다.
4. CDN 환경에서 발생하는 이슈 정리
CDN은 메인 웹사이트와 다른 출처(Cross-Origin)인 경우가 대부분이기 때문에, 이 두 정책에 모두 영향을 받습니다.
| 리소스 위치 | 로드 방법 | 발생 가능한 충돌 | 해결책 |
|---|---|---|---|
| CDN (정적 파일) | <script>, <link>, <img> 태그 (Embedding) |
CORP 충돌 ( same-origin 설정 시 로드 차단) |
CDN 응답 헤더에 Cross-Origin-Resource-Policy: cross-origin 설정. |
| CDN (동적 데이터) | Fetch/XHR 스크립트 (Data Reading) | CORS 충돌 (스크립트의 응답 접근 차단) |
CDN/원본 서버 응답 헤더에 Access-Control-Allow-Origin: <사이트 출처> 설정. |
💡 핵심: CDN과 same-origin의 역설
CDN에 호스팅 된 리소스에
Cross-Origin-Resource-Policy: same-origin을 설정하면, 브라우저는 해당 리소스가 오직 CDN 도메인에서만 사용될 수 있다고 해석합니다.따라서 메인 웹사이트(다른 출처)에서 로드하려고 할 때, 이
same-origin정책 때문에 로드가 차단되어 오히려 문제가 발생합니다. CDN 리소스는 Cross-Origin 로드를 허용하는cross-origin으로 설정해야 정상적으로 작동합니다.
5. 핵심 용어 정리
| 용어 | 정의 | 역할 |
|---|---|---|
| 출처 (Origin) | 프로토콜, 호스트, 포트 3가지가 결합된 URL의 일부. | 웹 보안의 기본 단위. |
| SOP (Same-Origin Policy) | 동일 출처에서 로드된 문서만 다른 출처의 리소스에 스크립트를 통해 접근하도록 허용하는 브라우저의 보안 정책 (기본적으로 교차 출처 스크립트 접근 차단). | 기본적인 보안 경계를 설정. |
| CORP (Cross-Origin Resource Policy) | 서버 응답 헤더를 통해 리소스를 포함(embedding)하거나 읽을 수 있는 출처를 명시적으로 제한하는 정책. (주로 사이드 채널 공격 방어 목적). | 리소스의 교차 출처 로드/사용 가능 여부를 제어. |
| CORS (Cross-Origin Resource Sharing) | SOP의 제약을 우회하여 스크립트 기반의 교차 출처 데이터 요청(XMLHttpRequest, Fetch API 등)을 안전하게 허용하기 위한 메커니즘. | SOP의 예외를 만들어 교차 출처 통신을 가능하게 함. |
| CDN (Content Delivery Network) | 지리적으로 분산된 서버 네트워크를 통해 콘텐츠를 빠르게 제공하는 서비스. | 웹사이트의 정적 파일을 교차 출처 환경에서 제공하게 만듦. |
댓글남기기