| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- Kubernetes
- DevOps
- backend
- CSS
- version-control
- API
- SRE
- HTTP
- architecture
- 버전관리
- Infra
- reliability
- Git
- Debugging
- Security
- Performance
- web
- CI
- Microservices
- react
- 성능
- frontend
- aws
- Operations
- auth
- JavaScript
- observability
- database
- NextJS
- Ops
- Today
- Total
고민보단 실천을
보안 헤더 실전: CSP/HSTS/X-Content-Type-Options를 언제 어떻게 켤까 본문
보안 헤더 실전: CSP/HSTS/X-Content-Type-Options를 언제 어떻게 켤까
보안 헤더는 켜면 좋지만, 잘못 켜면 서비스가 깨집니다. 그래서 '순서'가 중요합니다.
CSP/HSTS 등 자주 쓰는 헤더를 운영 가능하게(깨지지 않게) 도입하는 방법을 정리합니다.
이 글의 목표는 '개념 정리'보다, "어떤 기준으로 결정할지"와 "어떻게 운영에서 사고를 줄일지"를 남기는 것입니다.
왜 이게 어려운가(운영 관점)
보안은 '켜면 끝'이 아니라, 환경(도메인/HTTPS/프록시)과 결합된 실제 동작이 중요합니다. 그래서 단계적 도입과 관측이 핵심입니다.
보안 설정은 예외가 생기기 쉬우므로, 예외를 '운영 프로세스'로 관리(만료/승인/감사)하지 않으면 시간이 지날수록 사고 확률이 커집니다.
실전 내용(바로 적용)
보안 헤더는 켜면 좋지만, 잘못 켜면 서비스가 깨집니다. 그래서 '순서'가 중요합니다.
CSP/HSTS 등 자주 쓰는 헤더를 운영 가능하게(깨지지 않게) 도입하는 방법을 정리합니다.
핵심 요약(결론부터)
- 기준(정책)을 먼저 정하고, 구현/도구는 그 다음에 선택한다
- 실패/예외/회귀를 운영 체크리스트로 막는다
- 공식 문서를 최종 근거로 삼고, 팀 문서로 재가공한다
이 글이 해결하는 문제
보안 헤더를 '좋다더라'로 켰다가 로그인/결제/외부 스크립트가 깨지는 사고를 줄인다
단계적으로(관측 -> 적용 -> 강화) 도입하는 기준을 만든다
핵심 헤더 5개만
이 섹션은 선택지를 비교하거나 팀 합의가 필요한 기준을 정리하는 파트입니다. 표/예시를 기준으로 우리 팀의 기본값을 정해두면, 같은 토론을 반복하지 않게 됩니다.
도입 순서(추천)
- 1) 안전한 것부터: X-Content-Type-Options, Referrer-Policy
- 2) HSTS는 도메인/서브도메인/프리로드까지 검증 후
- 3) CSP는 Report-Only로 시작해 위반 로그를 수집한 뒤 강화
자주 깨지는 케이스
- CSP에서 analytics/ads 스크립트가 막혀 화면이 흰색이 됨
- HSTS includeSubDomains를 켰는데 일부 서브도메인이 HTTPS 미지원
- nosniff 때문에 잘못된 Content-Type 응답이 바로 깨짐
핵심 헤더 요약 표
| 헤더 | 목적 | 주의점(실무) |
|---|---|---|
| Content-Security-Policy | XSS/인젝션 완화 | Report-Only로 시작, nonce/해시 전략 필요 |
| Strict-Transport-Security | HTTPS 강제 | includeSubDomains/프리로드는 되돌리기 어렵다 |
| X-Content-Type-Options | MIME sniffing 방지 | 서버가 Content-Type을 정확히 내려야 함 |
| Referrer-Policy | 리퍼러 노출 제어 | 외부 서비스 연동에 영향 가능 |
| Permissions-Policy | 브라우저 기능 권한 제한 | 기능별 허용/차단을 명시 |
헤더 예시(발췌)
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint운영 체크리스트(바로 적용)
- CSP는 Report-Only로 1주 이상 로그를 보고 위반을 정리한다
- HSTS는 서브도메인/레거시 도메인까지 HTTPS가 보장되는지 확인한다
- 프록시/CDN이 헤더를 덮어쓰거나 누락하지 않는지 확인한다
- 보안 헤더 변경은 롤백 계획(배포/설정)을 같이 준비한다
FAQ
Q. CSP를 켜면 왜 갑자기 화면이 깨지나요?
A. 대부분 외부 스크립트/인라인 스크립트가 정책에 걸립니다. Report-Only로 위반 항목부터 수집하고, nonce/해시 정책으로 이동하는 게 안전합니다.
Q. HSTS는 한 번 켜면 끝인가요?
A. max-age와 프리로드 여부에 따라 되돌리기 어렵습니다. 서브도메인까지 포함할지, 운영/도메인 소유권까지 포함해 결정해야 합니다.
마무리: 다음 액션
- 팀의 기본값(정책)을 1페이지로 문서화한다
- 대표 시나리오 1개를 코드/설정 예시로 고정한다
- 배포 전후 지표(p95/p99, 오류율)를 비교해 효과를 확인한다
자주 하는 실수(사고 패턴)
- 보안 설정을 일괄 적용하고, 깨지는 곳을 땜질한다(원인 추적이 불가능해짐)
- 테스트/로컬에서만 되는 설정을 프로덕션에 그대로 반영한다(도메인/HTTPS 차이)
- 민감정보(토큰/비밀번호)를 로그에 남겨 2차 사고를 만든다
- 예외를 무기한으로 두고, 소유자가 없는 정책을 만든다
적용 순서(추천)
- 1) 무엇을 막는지(위협 모델)를 한 줄로 적는다(CSRF/XSS/탈취/권한 상승 등)
- 2) 기본값을 보수적으로 정한다(예: Secure/HttpOnly/SameSite 등)
- 3) 예외는 만료일/사유/승인자를 남긴다
- 4) E2E로 확인한다(도메인/HTTPS/프록시 포함)
- 5) 보안 이벤트를 지표/알람으로 운영한다(로그인 실패, 권한 거부, 토큰 재사용 등)
검증/회귀 방지
- 브라우저 DevTools에서 쿠키/헤더가 실제로 어떻게 붙는지 확인
- 프록시 뒤에서 HTTPS 인식이 올바른지 확인(X-Forwarded-Proto 등)
- 민감정보가 로그에 남지 않는지 샘플링 점검
팀 합의 템플릿(복붙용)
# 운영 규칙 템플릿(복붙용)
기본 정책: (예) 쿠키 HttpOnly; Secure; SameSite=Lax
예외 정책: (예) SameSite=None 필요 시 Secure 필수
감사/승인: 정책 변경은 PR + 승인 필수
복구: 계정 잠김/2FA 분실/권한 오류 대응 절차 링크FAQ
Q. CSP를 켜면 왜 갑자기 화면이 깨지나요?
A. 대부분 외부 스크립트/인라인 스크립트가 정책에 걸립니다. Report-Only로 위반 항목부터 수집하고, nonce/해시 정책으로 이동하는 게 안전합니다.
Q. HSTS는 한 번 켜면 끝인가요?
A. max-age와 프리로드 여부에 따라 되돌리기 어렵습니다. 서브도메인까지 포함할지, 운영/도메인 소유권까지 포함해 결정해야 합니다.
참고/출처
버전/환경에 따라 동작이 달라질 수 있으니, 최종 기준은 공식 문서를 확인합니다.
