프로덕션 주소에 콘텐츠 보안 정책(CSP)이 없으면, 악성 스크립트 한 줄이 사용자 브라우저에서 그대로 돈다. 전송 보안(HSTS)이 없으면 암호화되지 않은 접속이 한 번만 섞여도 쿠키가 평문으로 나갈 수 있다. 배포 전에 켤 응답 헤더는 열 개가 넘지 않는다. 표 한 장과 점검 목록만 채워도 보안 감사·침투 테스트 전에 “헤더 없음” 등급은 대부분 사라진다.
국내 서비스라도 CDN·클라우드 앞단을 쓰면 헤더를 앱이 쓰는지 엣지가 쓰는지부터 갈라야 한다. 권한·토큰 설계는 API 보안 체크리스트를, 비밀값·환경변수는 시크릿 프로덕션 체크리스트를, 앱 라우터 배포 전반은 Next.js 프로덕션 체크리스트를 같이 보면 빈칸이 줄어든다. 이 글 범위는 브라우저가 읽는 응답 헤더다.
배포 전 최소 세트 | 헤더 표 한 장
첫 스프린트에서 모든 정책을 완벽히 쓰지 않는다. 아래 여덟 줄을 “있음/없음”으로 먼저 찍는다. 값이 조금 틀려도 헤더 자체가 없는 상태보다는 낫다. 틀린 값은 스테이징에서 고치면 된다. 중요한 건 프로덕션 HTML 문서 응답에 최소한의 울타리가 보이느냐다.
헤더(약칭)
역할 한 줄
없으면 생기는 일
콘텐츠 보안 정책(CSP)
스크립트·스타일·프레임 출처 제한
인라인 스크립트 삽입·악성 외부 파일이 그대로 실행
전송 보안(HSTS)
이후 접속을 암호화 채널로 강제
중간자·혼합 콘텐츠 위험이 커짐
콘텐츠 타입 고정(nosniff)
브라우저가 타입을 추측해 실행하지 못하게 함
잘못된 타입 파일이 스크립트로 오인될 여지
프레임 차단
타 사이트 안에 우리 화면을 끼워 넣기 차단
투명 겹침 화면으로 클릭 유도(클릭재킹)
리퍼러 정책
외부로 나가는 이전 주소 정보 범위 제한
내부 경로·쿼리가 외부 로그에 남을 수 있음
권한 정책
카메라·마이크·위치 등 기능 기본 차단
임베드·외부 스크립트가 민감 기능을 요청 가능
오프너 격리
다른 탭과의 창 참조 관계를 끊음(권장)
탭 간 일부 공격면이 넓어짐
리소스 교차 읽기 제한
다른 출처가 우리 리소스를 읽기 어렵게 함(권장)
사이드 채널·리소스 탈취 완화가 약해짐
오픈웹 애플리케이션 보안 프로젝트(OWASP) 권장 묶음과 브라우저 문서(MDN)가 공통으로 꼽는 목록이다. 세부 문법 출처는 글 하단 참고 자료에 둔다. 교차 출처 자원 공유(CORS)도 응답 헤더지만, 브라우저 사전 요청 규칙이 달라 CORS 가이드를 따로 보는 편이 낫다.
실무에서 자주 하는 실수는 “API JSON에만 헤더를 넣고 HTML 문서에는 안 넣는 것”이다. 스크립트 삽입 피해는 대부분 문서 응답에서 난다. 반대로 JSON만 주는 순수 백엔드라면 콘텐츠 보안 정책의 체감 효과는 작고, 타입 고정·캐시·교차 출처 정책이 더 자주 쓰인다. 팀 문서에 “문서 응답 / API 응답” 두 줄로 정책을 나눠 적어두면 이후 PR 리뷰가 빨라진다.
한 줄 판정 — 헤더가 없는 배포는 “나중에 할 보안 작업”이 아니라 “기본 울타리 미설치”에 가깝다. 최소 세트부터 켠 뒤 정책을 조인다.
콘텐츠 보안 정책 작성 순서 | 관측 후 차단
콘텐츠 보안 정책을 하루 만에 “완벽한 화이트리스트”로 쓰려다 화면이 하얗게 되면, 팀은 헤더 자체를 끈다. 순서를 고정하는 편이 싸다. 보고만 하는 모드로 관측 → 위반 로그 약 1주 → 실제 차단 모드로 승격이 현장 감각에 맞다.
보고 전용 헤더 이름과 차단용 헤더 이름은 다르다. 처음 주에는 차단을 걸지 않고, 어떤 스크립트·연결이 막힐 뻔했는지 로그만 모은다. 로그에 “우리 도메인 / 광고·분석 / 예전 인라인 코드”를 세 갈래로 분류하면, 다음 주 허용 목록 회의가 짧아진다. 인라인 스크립트가 많으면 임의 허용(unsafe-inline)으로 도망가지 말고, 요청마다 바뀌는 일회용 값(nonce)이나 해시로 옮길 후보부터 이슈에 적는다.
기본 출처를 자기 도메인으로 두고, 스크립트·스타일·이미지·연결·글꼴·프레임 출처를 각각 채운다. 광고·분석 도메인은 “https 전체 허용” 같은 넓은 규칙 대신 호스트 단위로만 연다. 보고서 받는 주소가 있으면 소유 팀·보관 기간·개인정보 마스킹 규칙을 같이 적는다. 바이브코딩·자동 생성 HTML에 인라인 클릭 핸들러가 많으면 헤더 전에 마크업부터 정리한다. 관련 항목은 바이브코딩 보안 체크리스트에 있다.
프레임 조상 허용(frame-ancestors)을 정책 안에 넣으면 구형 프레임 차단 헤더와 역할이 겹친다. 둘 다 두어도 되고, 최신 브라우저는 프레임 조상 규칙을 우선한다. 폼 제출 목적지·base 태그 출처·플러그인 객체도 막아 두면 “문서 중간 삽입” 계열 사고가 줄어든다. 이미지 출처를 너무 넓히면 외부 추적 픽셀이 쉽게 들어온다. 아바타·미리보기 이미지가 많은 서비스만 예외를 두고, 가능하면 허용 저장소 호스트를 좁힌다.
콘텐츠 보안 정책은 문서 응답 헤더에서 확인하고, 위반은 콘솔·리포트 주소로 모은다
전송 보안과 쿠키 | 암호화 전제부터
전송 보안 헤더는 “다음부터 암호화되지 않은 주소로는 시도하지 말라”는 브라우저 기억이다. 인증서가 자주 바뀌는 스테이징에 긴 유효 기간을 걸면, 팀원 브라우저가 며칠 동안 접속을 거부할 수 있다. 운영과 스테이징 정책을 반드시 나눈다.
운영 환경에서는 보통 1년 단위 기억 시간을 쓰고, 하위 도메인까지 포함할지는 인증서·서비스 준비 상태를 보고 정한다. 사전 등록 목록(preload)에 올리면 빼는 데 시간이 걸린다. 사내 도구·와일드카드 인증서가 흔들리는 도메인은 사전 등록을 미룬다. 세션 쿠키에는 암호화 채널 전용·스크립트 읽기 금지·적절한 동일 사이트 범위를 붙인다. 이름이나 경로에 비밀값을 넣지 말고, 토큰은 본문·인증 헤더 쪽으로 옮기는 계획을 세운다.
로드밸런서에서 암호화를 끝내고 앱에는 평문 포트로 넘기는 구성이면, 앱이 “이미 안전한 접속”으로 아는지 확인한다. 여기가 어긋나면 리다이렉트가 무한 반복되거나, 쿠키 보안 플래그가 빠진다. 페이지 안에 암호화되지 않은 이미지·스크립트가 남아 있는지도 콘솔에서 한 번 본다. 혼합 콘텐츠가 있으면 전송 보안만 켠 효과가 반감된다.
주의 — 전송 보안 사전 등록 목록에 올리면 철회에 시간이 걸린다. 인증서가 불안정한 도메인은 운영 헤더만으로도 충분하다.
클릭재킹·임베드 | 프레임 정책을 하나로
결제·설정·관리자 화면이 다른 사이트 프레임 안에 들어가면, 사용자는 우리 버튼을 누르는 줄 알고 실제로는 공격자 레이어를 누를 수 있다. 위젯·미리보기처럼 임베드가 제품 기능인 경우만 예외 출처를 연다. 예외 없이 “전체 허용”은 쓰지 않는다.
기본값은 프레임 안에 넣지 않음, 또는 같은 출처만 허용이다. 구형 클라이언트를 고려하면 전통적인 프레임 옵션 헤더를 같이 둘 수 있다. 값이 서로 모순되면(한쪽은 허용, 한쪽은 차단) 디버깅이 길어진다. 파트너 임베드가 필요하면 허용 호스트를 명시하고, 임베드 전용 경로만 완화한다. 그 경로에는 로그인 세션 쿠키가 붙지 않게 경로·동일 사이트 속성을 분리한다.
관리자·결제·비밀번호 변경 주소는 예외 없이 프레임 차단이 맞다. 외부 로그인 팝업 흐름이 있으면 창 참조 격리 정책과 함께 본다. 인증 흐름 자체는 OAuth 실무 가이드 쪽 체크와 맞추면 빈칸이 줄어든다.
프레임 정책은 제품 임베드 요구가 생기기 전에 기본 차단으로 두는 편이 싸다
타입 고정·리퍼러·권한 | 자주 빠지는 세 줄
콘텐츠 보안 정책과 전송 보안만 넣고 끝내면, 감사 리포트에 같은 잔여 항목이 반복된다. 아래 세 줄은 설정 비용이 낮고 효과가 분명하다. 전역 기본값으로 넣고, 기능이 있는 페이지만 예외를 연다.
콘텐츠 타입 고정은 문서·API·정적 파일 응답에 공통으로 건다. 리퍼러 정책은 외부로 나가는 이전 주소 정보를 줄여, 내부 경로가 광고·분석 로그에 남는 일을 막는다. 권한 정책은 카메라·마이크·위치·결제 등 쓰지 않는 기능을 기본 차단한다. 화상 상담·지도 첨부처럼 꼭 필요한 화면만 해당 출처에 기능을 연다.
사용자가 파일을 올리는 서비스라면 서버가 타입을 결정하고, 실행 가능 확장자는 저장소·배포망에서 막는다. 다운로드 응답에는 저장 파일 이름과 올바른 타입을 명시해 브라우저가 스크립트로 오인하지 않게 한다. 업로드·의존성 쪽 공급망 이슈는 Supply Chain 방어 가이드와 연결해 본다.
앱·엣지 어디에 넣나 | 진실 공급원은 하나
헤더를 앱·CDN·웹서버에 동시에 쓰면 값이 덮이거나 중복되어 디버깅이 길어진다. “최종으로 브라우저에 나가는 줄”의 주인을 하나만 고른다. 국내 트래픽이 많은 서비스도 해외 CDN을 앞에 두는 경우가 많아, 원본 서버 설정만 보고 안심하면 안 된다. 실제 사용자 응답을 한 번 덤프해 확인한다.
Next.js 계열이면 설정 파일의 공통 헤더 함수 또는 미들웨어에서 문서 HTML에 squirt 넣는다. 경로 매처로 API와 페이지를 나눌 수 있다. Node 서버 프레임워크면 전역 훅 한 곳에 모은다. 헬퍼 라이브러리를 쓰면 기본값을 읽고 팀에 맞게 덮어쓴다. 정적 사이트·리버스 프록시면 웹서버 설정이 롤백이 쉽다. CDN 엣지 규칙으로 강제할 때는 원본 헤더와 충돌 테스트를 반드시 한다.
요청마다 바뀌는 일회용 값(nonce) 기반 정책은 순수 정적 헤더 파일만으로는 부족하다. 미들웨어나 서버 렌더 레이어가 필요하다. 미리보기(PR) 환경에도 같은 헤더를 켜 두면 “운영만 깨짐”을 줄인다. 배포 순서·헬스체크 흐름에 “헤더 스모크 한 줄”을 붙이려면 프로덕션 배포 체크리스트와 같은 템플릿에 칸을 하나 추가하면 된다.
헤더는 앱·CDN·웹서버 중 한 레이어를 주인으로 정하고 나머지는 비운다
스테이징 검증 순서 | 덤프·브라우저·리포트
헤더 PR은 “설정 파일 커밋”으로 끝내지 않는다. 배포 후 5분 점검 또는 자동 잡에 아래 순서를 붙인다. 홈·로그인·핵심 API 세 경로의 응답 헤더 덤프를 저장해 두면, 나중에 “언제 빠졌는지” 비교가 가능하다.
브라우저 개발자 도구에서 문서 응답을 열어 콘텐츠 보안 정책·전송 보안 값이 보이는지 확인한다. 콘솔에 정책 위반이 쏟아지면, 알려진 서드파티인지 우리 코드인지 먼저 가른다. 로그인 → 핵심 저장 → 결제(또는 동등한 쓰기) 한 바퀴를 사람이 눌러 본다. 버튼이 죽은 경우가 대부분 스크립트 차단이다.
광고·채팅·결제 위젯이 막히면 바로 광범위 허용으로 풀지 말고, 필요한 호스트만 보안 리뷰 후 추가한다. 외부 보안 헤더 점수 사이트나 자체 스크립트로 전후 등급을 남긴다. 보고 전용 단계라면 리포트 주소에 샘플이 들어오는지 확인한다. 컨테이너 이미지·의존성 스캔은 별 트랙이다. 이미지 쪽은 이미지 스캔 가이드를 본다.
자주 깨지는 패턴 | 피하면 되는 여섯 가지
도구 점수만 맞추려고 넣은 설정이 오히려 보호를 약화시키는 경우가 있다. 아래는 점검표에 “금지” 칸으로 고정해 두는 편이 낫다.
스크립트 출처에 전체 허용이나 “모든 https”를 넣으면 점수는 올라가 보여도 삽입 공격 완화 효과가 거의 없다. 보고 전용 모드만 수개월 방치하면 차단은 영원히 안 온다. 달력에 승격일을 박는다. 인라인·동적 실행 임의 허용을 “임시”로 남기면 그게 기본값이 된다. 이슈 번호와 제거 분기를 적는다.
API에만 헤더를 넣고 HTML 문서에는 없는 구성, 스테이징에 긴 전송 보안 기억 시간, 디버그 로그에 인증 헤더를 찍는 습관도 반복된다. 주입·인증 문제는 헤더만으로 끝나지 않는다. 웹 취약점 상위 목록 점검은 OWASP 체크리스트와 병행한다.
범위 고정 — 이번 스프린트 목표는 “최소 여덟 헤더 존재 + 콘텐츠 보안 정책 보고 모드 1주”면 충분하다. 완벽한 화이트리스트는 다음 스프린트로 옮긴다.
2주 도입 순서 | 관측 후 차단
보안 헤더 작업은 범위가 커 보이지만, 문서 한 장과 풀 리퀘스트 두 개로 끝낼 수 있다. 처음부터 전사 표준을 만들지 말고, 대표 서비스 하나·문서 응답부터 닫는다. 2주 캘린더는 아래처럼만 맞춘다.
첫째 날에는 운영 응답을 덤프해 없는 항목과 주인 레이어(앱인지 CDN인지)를 정한다. 그다음 이틀은 타입 고정·리퍼러·프레임·권한 기본값을 올린다. 중반에는 운영에만 긴 전송 보안을 넣고 쿠키 플래그를 감사한다. 후반 일주일은 콘텐츠 보안 정책을 보고 모드로 돌리고 위반을 분류한 뒤, 인라인 정리와 최소 호스트 추가를 끝낸다. 마지막 이틀에 차단 모드로 올리고 롤백 방법을 런북에 세 줄 적는다. 네트워크 접근 통제·장비 정책은 별 트랙이다. 필요하면 제로 트러스트 가이드를 본다.