본문 바로가기
Web Security

[Web Security] Same-Origin Policy(SOP) 기초와 실무 가이드: CORS·COOP/COEP까지

by Tru5tC0der 2025. 8. 21.

현대 웹 애플리케이션에서 Same-Origin Policy (SOP)는 한 출처에서 로드된 문서나 스크립트가 다른 출처의 리소스와 상호작용하는 방식을 제한하는 핵심 보안 메커니즘입니다. 동일 출처(Origin)는 scheme(프로토콜) + host(호스트) + port(포트)가 모두 일치할 때만 성립합니다.


목차

  • Origin의 정의와 비교 예시
  • Special Origins
  • SOP가 허용/차단하는 것
  • 임베딩 가능한 리소스와 제약
  • CORS: 교차 출처 읽기를 안전하게 허용하는 메커니즘
  • Cross-window communication: postMessage 보안
  • 공격·우회 시나리오와 방어 전략
  • 현대 브라우저 보완 정책 (SOP 보강)
  • 로컬 개발 시 주의 (file://)
  • 실무 체크리스트

Origin의 정의와 비교 예시

동일 출처프로토콜 · 호스트 · 포트가 모두 일치해야 합니다.

기준 URL: http://store.company.com/dir/page.html

URL 결과 이유
http://store.company.com/dir2/other.html ✅ 동일 경로만 다름
https://store.company.com/page.html 프로토콜 다름
http://store.company.com:81/dir/page.html 포트 다름
http://news.company.com/dir/page.html 호스트 다름

참고: 포트를 명시하지 않으면 각 프로토콜의 기본 포트(HTTP 80, HTTPS 443)가 적용됩니다.


Special Origins

  • about:blank, javascript:
    • 자체 origin 정보가 없어, 해당 문서를 연 컨텍스트의 origin을 상속합니다. 예: window.open('about:blank')로 연 후 document.write 등.
  • file://
    • 최신 브라우저는 로컬 파일을 불투명(opaque) origin으로 취급하는 경향이 강합니다. ES 모듈, XHR/Fetch 등에서 CORS 관련 제약이 발생할 수 있습니다.

SOP가 허용/차단하는 것

  • Cross-Origin Writes — 보통 허용: 링크, 리다이렉트, 폼 제출 등
  • Cross-Origin Embedding — 보통 허용: <img>, <script>, <link rel="stylesheet">, <iframe>
  • Cross-Origin Reads — 원칙적으로 차단: XHR/Fetch로 응답 본문을 읽으려면 CORS 허용 필요

임베딩 가능한 리소스와 제약

리소스 태그/방식 핵심 제약
JS (클래식) <script src="..."> 교차 출처 로드 가능합니다. 에러 상세는 마스킹되며, crossorigin + 서버 CORS 설정 시 자세한 에러를 확인할 수 있습니다.
JS (모듈) <script type="module" src="..."> 교차 출처일 경우 CORS가 필수입니다(없으면 로드 차단). 로컬 file://에서 실패 사례가 많습니다.
CSS <link rel="stylesheet" href="..."> 임베딩 가능합니다. 하지만 CSSOM 접근(cssRules 등)은 동일 출처이거나 CORS 허용이 필요합니다.
이미지 <img src="..."> 임베딩 가능합니다. canvas에 그리면 taint되어 픽셀 접근이 차단됩니다(서버에서 CORS 허용 시 해제 가능).
폰트 @font-face, <link rel="preload" as="font"> 교차 출처 폰트 사용 시 CORS가 필요합니다(Access-Control-Allow-Origin).
미디어 <video>, <audio> 임베딩 가능합니다. 일부 추출/처리 API는 추가 제약이 존재합니다.
프레임 <iframe src="..."> 임베딩은 가능하나 서버의 CSP frame-ancestors, X-Frame-Options, 또는 CORP 설정으로 제한할 수 있습니다.

CORS: 교차 출처 읽기를 안전하게 허용하는 메커니즘

서버가 응답 헤더로 허용 출처를 명시하면 브라우저는 교차 출처 응답을 노출합니다.

서버 예시 (Express)

app.use((req, res, next) => {
  const origin = 'https://trusted.example';
  res.header('Access-Control-Allow-Origin', origin);
  res.header('Vary', 'Origin');
  res.header('Access-Control-Allow-Credentials', 'true');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  next();
});

주의: *Access-Control-Allow-Credentials: true 는 함께 사용할 수 없습니다. 여러 출처를 지원하려면, 요청 헤더의 Origin 값을 동적으로 읽어 허용 목록과 비교한 후, 일치하는 경우에만 해당 Origin 값을 Access-Control-Allow-Origin 헤더에 설정해야 합니다.

Preflight 예시

Request (OPTIONS /api/data)

Origin: https://app.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, X-Custom

Response

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, X-Custom
Access-Control-Max-Age: 86400

Cross-window communication: postMessage 보안

서로 다른 origin의 window/iframe 간에는 DOM 접근이 차단됩니다. 통신이 필요하면 postMessage 를 사용하되, 반드시 event.origin 검증과 명확한 데이터 스키마 검증을 수행해야 합니다.

과거의 document.domain 기반 완화는 권장하지 않습니다. 대안으로 postMessage 또는 브라우저 제공 채널을 사용해야 합니다.


공격·우회 시나리오와 방어 전략

  • CSRF 방어: CSRF 토큰과 SameSite 쿠키를 사용합니다. 민감한 동작은 POST 전용으로 제한하고, 서버 측에서 Origin 또는 Referer 헤더를 검증하는 것이 좋습니다.
  • JSON 하이재킹: 민감한 JSON 리소스는 인증을 요구하고, Content-Type: application/json 헤더를 사용하며, GET 메서드로 제공하지 않도록 설계합니다.
  • 클릭재킹 방어: Content-Security-Policy: frame-ancestors 'self' https://partner.example 사용을 권장합니다. 구형 브라우저 호환을 위해 X-Frame-Options: DENY 또는 SAMEORIGIN을 병행할 수 있습니다.

현대 브라우저 보완 정책 (SOP 보강)

  • COOP / COEP (Cross-Origin-Opener/Embedder Policy)
    • 고성능 기능(예: SharedArrayBuffer) 사용을 위해 문서를 격리합니다.
    • 예: Cross-Origin-Opener-Policy: same-origin / Cross-Origin-Embedder-Policy: require-corp
  • CORP (Cross-Origin-Resource-Policy)
    • 리소스 단위에서 교차 출처 임베딩을 제어합니다.
    • 예: Cross-Origin-Resource-Policy: same-origin 또는 same-site
  • Fetch Metadata 요청 헤더
    • Sec-Fetch-Site, Sec-Fetch-Mode, Sec-Fetch-Dest 등으로 요청의 출처 성격을 판단해 차단 규칙을 적용할 수 있습니다.

로컬 개발 시 주의 (file://)

  • ES 모듈(<script type="module">)은 file:// 환경에서 CORS 제약으로 실패하는 경우가 많습니다. 로컬에서는 간단한 HTTP 서버(npx http-server 등)를 사용합니다.
  • 클래식 <script> 는 로드될 수 있으나, file:// 은 불투명 origin이므로 XHR/Fetch 등에 제한이 생깁니다.

실무 체크리스트

  • 교차 출처 읽기는 반드시 CORS로 통제, Credentials 필요 시 특정 Origin만 허용
  • <script type="module">는 교차 출처 CORS 필수. 로컬 개발은 HTTP 서버 사용
  • 이미지 canvas 사용은 CORS로 허용하거나, 접근 필요 없다면 그대로 사용
  • CSSOM 접근이 필요하면 CORS로 스타일시트 제공
  • 웹폰트는 Access-Control-Allow-Origin 설정
  • 클릭재킹 방어: CSP frame-ancestors + X-Frame-Options 병행
  • postMessage 수신 시 event.origin 검증 + 데이터 스키마 확인
  • document.domain 사용 금지. 대안은 postMessage
  • 고권한 기능 필요 시 COOP/COEP로 cross-origin isolation 구성
  • 서버에서 Fetch Metadata 헤더 기반 차단 규칙 도입

참고 자료