본문으로 건너뛰기
김신건의 로그

[Network] CORS: Same-Origin Policy, preflight, credentials

· 수정 · 📖 약 2분 · 604자/단어 #cors #security #browser #network #backend
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

URLOrigin
https://app.example.com/pagehttps://app.example.com
https://app.example.com:443https://app.example.com (default port)
http://app.example.comhttp://app.example.com (scheme 다름)
https://api.example.comhttps://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)
  • ReadableStream body 없음

→ 위 조건 하나라도 어기면 preflight. JSON Content-Type: application/jsonpreflight 유발.

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-Methodspreflight 응답에서 허용 method
Access-Control-Allow-Headers허용 헤더
Access-Control-Expose-HeadersJS 가 읽을 수 있는 응답 헤더
Access-Control-Max-Agepreflight 캐시 (초)
Access-Control-Allow-Credentialstrue 면 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: trueRFC 위반. 브라우저가 거절. credentials 가 필요하면 명시적 origin.

흔한 함정

WARNING

  1. Allow-Origin: *모든 경우에 박음 = credentials 모드에서 동작 안 함.
  2. preflight 응답에 200 보냄 = 보통 204 No Content 가 정석. 200 도 동작은 함.
  3. Vary: Origin 누락 = CDN 이 다른 origin 응답 을 캐시. 모든 사용자에게 같은 origin 의 응답 이 감.
  4. CORS 가 서버 보안 이라고 오해 = 브라우저 보안. curl 은 CORS 무시. 서버 보안은 별도 (auth, IP allowlist 등).
  5. 로컬 개발 환경 (localhost:3000) 만 허용 으로 프로덕션 cross-origin 미테스트.

CORS vs CSRF

CORSCSRF
본질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…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기