[Network] CORS: Same-Origin Policy, preflight, credentials
CORS, Cross-Origin Resource Sharing, Same-Origin Policy, preflight, OPTIONS request, Access-Control-Allow-Origin
정의
CORS (Cross-Origin Resource Sharing) 는 Same-Origin Policy (SOP) 의 완화 메커니즘. 브라우저가 origin 이 다른 리소스에 조건부로 접근 할 수 있게 한다.
IMPORTANT
CORS 는 브라우저의 보안 모델. 서버 → 서버 호출에는 영향 없음. curl, Postman 도 CORS 적용 안 함. 오로지 브라우저가 JS 코드의 cross-origin fetch 를 막거나 허용한다.
Origin = scheme + host + port
| URL | Origin |
|---|---|
https://app.example.com/page | https://app.example.com |
https://app.example.com:443 | https://app.example.com (default port) |
http://app.example.com | http://app.example.com (scheme 다름) |
https://api.example.com | https://api.example.com (host 다름) |
Simple Request vs Preflight
flowchart TD
Q{요청이 Simple?}
Q -->|예| S[즉시 보냄]
Q -->|아니오| P[OPTIONS preflight 먼저]
S --> CHECK1[응답의 ACAO 검증]
P --> CHECK2[preflight 응답 검증]
CHECK2 -->|허용| Send[실제 요청 전송]
CHECK2 -->|거절| Block[브라우저 차단]
Simple Request 조건 (모두 만족):
- Method: GET, HEAD, POST
- 헤더: 안전한 헤더 +
Content-Type: application/x-www-form-urlencoded | multipart/form-data | text/plain - 이벤트 listener 없음 (XHR upload)
ReadableStreambody 없음
→ 위 조건 하나라도 어기면 preflight. JSON Content-Type: application/json 도 preflight 유발.
Preflight 흐름
sequenceDiagram
autonumber
participant B as Browser
participant S as Server
B->>S: OPTIONS /api/users<br/>Origin: https://app.example.com<br/>Access-Control-Request-Method: POST<br/>Access-Control-Request-Headers: content-type, authorization
S-->>B: 204 No Content<br/>Access-Control-Allow-Origin: https://app.example.com<br/>Access-Control-Allow-Methods: GET, POST, DELETE<br/>Access-Control-Allow-Headers: content-type, authorization<br/>Access-Control-Max-Age: 86400
Note over B: 캐시 (Max-Age)
B->>S: POST /api/users<br/>Origin: https://app.example.com<br/>Content-Type: application/json<br/>{...}
S-->>B: 201 Created<br/>Access-Control-Allow-Origin: https://app.example.com
Response 헤더 가이드
| 헤더 | 의미 |
|---|---|
Access-Control-Allow-Origin | 허용된 origin (하나만 또는 *) |
Access-Control-Allow-Methods | preflight 응답에서 허용 method |
Access-Control-Allow-Headers | 허용 헤더 |
Access-Control-Expose-Headers | JS 가 읽을 수 있는 응답 헤더 |
Access-Control-Max-Age | preflight 캐시 (초) |
Access-Control-Allow-Credentials | true 면 cookie/Authorization 허용 |
Credentials (cookie / Authorization)
fetch('https://api.example.com/me', {
credentials: 'include', // cookie 보내기
});
이 경우 서버는 반드시:
Access-Control-Allow-Origin: https://app.example.com ← * 불가능!
Access-Control-Allow-Credentials: true
CAUTION
*Allow-Origin: * + Allow-Credentials: true 는 RFC 위반. 브라우저가 거절. credentials 가 필요하면 명시적 origin.
흔한 함정
WARNING
Allow-Origin: *를 모든 경우에 박음 = credentials 모드에서 동작 안 함.- preflight 응답에 200 보냄 = 보통 204 No Content 가 정석. 200 도 동작은 함.
Vary: Origin누락 = CDN 이 다른 origin 응답 을 캐시. 모든 사용자에게 같은 origin 의 응답 이 감.- CORS 가 서버 보안 이라고 오해 = 브라우저 보안. curl 은 CORS 무시. 서버 보안은 별도 (auth, IP allowlist 등).
- 로컬 개발 환경 (
localhost:3000) 만 허용 으로 프로덕션 cross-origin 미테스트.
CORS vs CSRF
| CORS | CSRF | |
|---|---|---|
| 본질 | cross-origin fetch 허용 | cross-origin 의 위조 요청 방어 |
| 방향 | 브라우저 → 서버 허용 | 브라우저 → 서버 거절 |
| 토대 | Same-Origin Policy 의 완화 | Same-Origin Policy 의 우회 방어 |
| 도구 | Response 헤더 (ACAO) | CSRF token, SameSite cookie |
자세한 건 CSRF 참고.
서버 구현 예시 (Express)
import cors from 'cors';
app.use(cors({
origin: ['https://app.example.com', 'https://staging.example.com'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
exposedHeaders: ['X-Total-Count'],
maxAge: 86400,
}));
SOP 가 막는 것들
fetch('https://other.com')의 응답 읽기- 다른 origin iframe 의 DOM 접근
- 다른 origin 의 cookie 읽기
SOP 가 막지 않는 것 (CSRF 의 토대):
<form action="https://other.com">의 POST<img src="https://other.com/api">(GET)<script src="https://other.com/api">(실행됨)
관련 위키
이 글의 용어 (4개)
- [Auth] CSRF: 위조 요청 공격과 방어auth-security
- 정의 CSRF (Cross-Site Request Forgery) 는 사용자가 로그인된 상태 에서 공격자가 만든 페이지 가 피해자 브라우저로 위조 요청 을 보내는 공격. 쿠키 기…
- [Auth] JWT: 구조, 서명, 만료, refresh tokenauth-security
- 정의 JWT (JSON Web Token) (RFC 7519) 은 base64url 인코딩된 JSON + 서명 형태의 self-contained token. 서버가 세션 저장 없…
- [Auth] Session Cookie: HttpOnly, SameSite, Secure flagauth-security
- 정의 Session Cookie 는 서버가 발급한 임의 ID 를 쿠키에 보관. 매 요청 자동 첨부. 서버 측 세션 store (Redis, DB) 와 짝. [!IMPORTANT]…
- [Network] HTTP/1.1: keep-alive, pipelining, chunked transfernetwork
- 정의 HTTP/1.1 (1997, RFC 9112 으로 재정리) 는 텍스트 기반 stateless request/response 프로토콜. 2026 시점에도 대부분의 트래픽이 H…
이 개념을 다룬 위키 페이지 (6)
- wiki[Auth] CSRF: 위조 요청 공격과 방어
- wiki[Auth] JWT: 구조, 서명, 만료, refresh token
- wiki[Auth] Session Cookie: HttpOnly, SameSite, Secure flag
- wiki[Django 6.0] Content Security Policy: XSS 방어의 최종형
- wiki[K8s] Ingress: L7 라우팅, TLS termination, Gateway API
- wiki[K8s] Service: ClusterIP / NodePort / LoadBalancer / ExternalName
💬 댓글