
현대 웹 애플리케이션에서 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등.
- 자체 origin 정보가 없어, 해당 문서를 연 컨텍스트의 origin을 상속합니다. 예:
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 헤더 기반 차단 규칙 도입