codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›security

보안 헤더와 CORS

2026-04-28 게시· 2026-05-18 갱신·0회 조회

보안 헤더와 CORS

브라우저가 페이지를 그릴 때마다 여러 보호 메커니즘이 동시에 돕니다. 동일 출처 정책 · CORS preflight · CSP 의 스크립트 화이트리스트 · HSTS · Spectre 이후 도입된 격리 헤더. 이 글은 각 헤더의 역할과 자주 쓰이는 모양 · 점검 도구.

1. 동일 출처 정책

브라우저의 기본 보안 모델. 한 출처 (scheme://host:port) 의 스크립트가 다른 출처의 자원을 임의로 읽지 못하게. 이 위에 CORS · CSP · 격리 헤더가 쌓입니다.

2. CORS

다른 출처의 자원에 접근을 허용하는 메커니즘. RFC 6454 (Origin) · WHATWG Fetch Standard.

서버가 응답에 Access-Control-Allow-Origin 같은 헤더를 보내야 브라우저가 응답을 자바스크립트에 노출:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400

Preflight (OPTIONS) — 다음 조건 중 하나라도 해당하면 브라우저는 본 요청 전에 OPTIONS 메서드로 사전 요청:

  • 단순 메서드 (GET · HEAD · POST) 가 아닌 경우.
  • Content-Type 이 단순 값 (application/x-www-form-urlencoded · multipart/form-data · text/plain) 이 아닌 경우 (application/json 도 preflight 대상).
  • 사용자 정의 헤더 (Authorization 외) 가 있는 경우.

서버는 OPTIONS 에 적절한 Access-Control-* 응답을 보내야 본 요청 진행.

Credentials — 쿠키 · HTTP 인증 · 클라이언트 인증서를 포함한 요청은 credentials: 'include' 필요. 이 경우 서버 응답의 Access-Control-Allow-Origin 은 * 일 수 없음 — 명시적 출처 필요. Access-Control-Allow-Credentials: true 도 함께.

3. CSP

W3C CSP Level 2 (2016) · Level 3 (진행). 브라우저에 "이 페이지에서 어떤 출처의 스크립트 · 스타일 · 이미지 · 연결을 허용하는가" 를 선언. XSS 의 영향 범위를 좁히는 가장 효과적인 도구.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-rAnd0m';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';

nonce · hash · strict-dynamic:

  • nonce — 인라인 스크립트에 임의의 base64 nonce 를 넣고 CSP 에 같은 값 선언. 매 응답마다 새 값.
  • hash — 인라인 스크립트의 SHA-256 해시를 CSP 에 등록.
  • strict-dynamic — nonce 로 신뢰된 스크립트가 동적으로 로드한 스크립트도 신뢰. 화이트리스트 방식의 한계를 줄이는 모던 패턴.
script-src 'nonce-abc123' 'strict-dynamic';

Google 의 CSP Evaluator · CSP guide 가 strict-dynamic 모델 권장.

report-only — Content-Security-Policy-Report-Only 헤더로 위반을 차단하지 않고 보고만 받음. 신규 적용 시 회귀 점검 자리.

4. 다른 보안 헤더

HSTS (HTTP Strict Transport Security) — RFC 6797 (2012). HTTPS 강제:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  • max-age — 강제 기간 (초). 권장 1 년 이상.
  • includeSubDomains — 서브도메인까지.
  • preload — HSTS preload list 등재 후 브라우저가 첫 방문 전부터 강제.

preload 등록은 되돌리기 어렵다는 점이 자주 지적. 신중히.

X-Frame-Options 또는 frame-ancestors — iframe 임베딩 차단. clickjacking 방지:

X-Frame-Options: DENY
# 또는 CSP 의 frame-ancestors
Content-Security-Policy: frame-ancestors 'none'

CSP 의 frame-ancestors 가 더 표현력이 강해 X-Frame-Options 를 대체하는 흐름.

X-Content-Type-Options — 브라우저의 MIME sniffing 차단. application/json 응답이 HTML 로 해석돼 XSS 가 트리거되는 사고를 막음:

X-Content-Type-Options: nosniff

Referrer-Policy — 요청 시 Referer 헤더에 어디까지 정보를 담을지:

Referrer-Policy: strict-origin-when-cross-origin

no-referrer · same-origin · strict-origin-when-cross-origin (모던 기본값) 등.

Permissions-Policy — 이전 이름 Feature-Policy. 카메라 · 마이크 · 지오로케이션 등 브라우저 기능 사용을 제어:

Permissions-Policy: camera=(), microphone=(), geolocation=(self)

X-XSS-Protection — 레거시 헤더. 모던 브라우저는 무시 (Chrome 78에서 제거). CSP 가 대체.

5. 격리 헤더 (Spectre 이후)

2018 년 Spectre · Meltdown 이 하드웨어 사이드 채널 공격을 드러냄. 브라우저는 Cross-Origin Isolation 을 위해 새 헤더 도입.

COOP (Cross-Origin-Opener-Policy):

Cross-Origin-Opener-Policy: same-origin

다른 출처 윈도우와의 브라우저 컨텍스트 공유 차단. window.opener 같은 표면을 막음.

COEP (Cross-Origin-Embedder-Policy):

Cross-Origin-Embedder-Policy: require-corp

페이지가 로드하는 모든 자원이 명시적 권한 (CORP 헤더 또는 CORS) 을 가져야 함.

CORP (Cross-Origin-Resource-Policy):

Cross-Origin-Resource-Policy: same-site

자원 자신이 어떤 출처에서 임베드 가능한지 선언. COEP 환경에서 자원 측의 옵트인.

COOP + COEP 가 모두 충족되면 crossOriginIsolated 환경이 활성화되어 SharedArrayBuffer · 고해상도 타이머 같은 강력한 API 사용 가능. 일반 사이트에는 과한 수준일 수 있어 기능이 필요한 자리에 한정해 적용.

6. 미들웨어로 일괄

대부분의 프레임워크가 보안 헤더 미들웨어 제공:

  • Express — helmet.
  • Fastify — @fastify/helmet.
  • Next.js — next.config.js 의 headers() 또는 middleware.ts.
  • Spring Boot — Spring Security 의 headers() DSL.
  • nginx · Caddy — 서버 설정에서 일괄.

helmet 의 기본값이 합리적인 출발점.

7. CORS 좁은 적용

  • 출처 화이트리스트는 정확한 도메인.
  • 와일드카드는 익명 API 가 아니면 피함.
  • credentials + 와일드카드는 표준이 금지.
  • preflight 캐시 (Access-Control-Max-Age) 로 OPTIONS 비용 절감.

8. 점검 도구

  • securityheaders.com — 브라우저 응답 헤더 점수.
  • csp-evaluator.withgoogle.com — CSP 정책 분석.
  • Mozilla Observatory — 종합 점검.
  • 브라우저 개발자 도구의 Security · Network 탭.

도입 후 정기 점검 — 새 기능 추가가 정책을 무력화할 수 있음.

9. 자주 걸리는 자리

CORS 의 OPTIONS 누락 — 백엔드가 OPTIONS 를 처리하지 않으면 모든 preflight 실패. 라우터 미들웨어로 일괄.

Access-Control-Allow-Origin: * + credentials — 표준 위반, 브라우저가 거부.

CSP 의 unsafe-inline — 사실상 XSS 보호가 사라짐. nonce / hash / strict-dynamic 으로.

HSTS preload 의 비가역성 — 잘못 설정하면 사용자 브라우저가 오랫동안 HTTP 접근을 거부.

X-Frame-Options 와 CSP frame-ancestors 의 충돌 — 두 헤더가 다른 정책을 보내면 더 엄격한 쪽이 적용되거나 브라우저별 동작이 다를 수 있음. 일관된 정책.

개발 환경의 느슨한 헤더가 운영에 새기 — 테스트용 와일드카드가 운영에 그대로 흘러가는 사고. 환경별 설정 분리.

nonce 재사용 — 매 응답 새 값이어야 의미. 정적 nonce 는 보호 효과 사라짐.

CDN 캐시와의 충돌 — nonce 가 든 응답이 캐시되면 같은 nonce 가 여러 사용자에게. XSS 표면 확대. 인증 페이지는 캐시 비활성.

하고픈 말

보안 헤더는 한 번 설정하면 그 이후의 코드가 자연스럽게 그 위에 쌓이는 토대. CSP strict-dynamic + HSTS preload + nosniff + Referrer-Policy + frame-ancestors 다섯 자리를 helmet · Caddy · next.config.js 의 일괄 설정으로 두고, securityheaders.com 으로 정기 점검. CORS 는 정확한 도메인 화이트리스트 + 적절한 preflight 응답.

Next

  • (security 끝)

MDN CORS · MDN CSP · Google Web Fundamentals Strict CSP · RFC 6797 HSTS · Cross-Origin Isolation Guide · securityheaders.com · helmet 공식 · OWASP Secure Headers Project 를 참고합니다.

security 카테고리의 다른 글

카테고리 전체 보기 →
  • 공개 라우트 화이트리스트 — 신규 도메인 도입 시 같이 갱신
  • 익명 폼 — 최소한의 안전망
  • 비밀번호 해싱 — bcrypt · scrypt · Argon2
  • 입력 검증 — 경계에서 다듬는다
  • 레이트 리밋 — 알고리즘과 구현
  • OAuth — state · PKCE · OIDC