본문 바로가기
MAKESITE
무료 스캔 문의하기 로그인

점검 항목 · 보안

CSP 헤더 미설정

없어서 걸리는 것보다 잘못 켜서 사고가 나는 항목입니다. 그래서 이 글의 절반은 켜는 방법이 아니라 켜는 순서에 대한 것입니다.

심각도 높음 보안 무료 스캔 판정 항목 /check/csp

01DEFINITION

한 줄로 말하면

CSP 는 "이 페이지는 어디서 온 스크립트만 실행한다"를 브라우저에 미리 알려 두는 헤더입니다. 없으면 게시판 글이나 댓글에 스크립트가 한 줄 끼어들었을 때 브라우저가 그것을 그대로 실행합니다. XSS 를 막는 마지막 그물이고, OWASP 2025 판에서 보안 설정 오류가 2위까지 올라온 배경이기도 합니다. 다만 처음부터 강제하면 멀쩡하던 기능이 조용히 죽으므로, 위반만 수집하는 Report-Only 로 며칠 관찰한 뒤 켜는 것이 순서입니다.

02RISK

없으면 무슨 일이 생기나

게시판이나 댓글이 있는 사이트를 생각해 보십시오. 입력값 처리에 구멍이 하나 생겨 누군가 글 안에 스크립트를 심으면, 그 글을 여는 모든 사람의 브라우저에서 그 스크립트가 우리 사이트의 권한으로 실행됩니다. 로그인 세션을 훔쳐 외부로 보내는 데 몇 줄이면 충분합니다. CSP 는 그 스크립트가 실행되기 직전에, 그리고 데이터가 밖으로 나가기 직전에 한 번 더 막습니다.

입력값 검증을 잘하면 되는 것 아니냐고 물으실 수 있습니다. 맞는 말이지만, 사이트에는 우리가 쓰지 않은 코드가 늘 섞여 있습니다. 게시판 스킨, 에디터 플러그인, 광고 스크립트, 채팅 위젯 가운데 하나가 취약하면 우리 검증은 통과된 상태로 사고가 납니다. CSP 가 마지막 그물이라고 불리는 이유가 이것입니다.

외부 스크립트가 바뀌는 경우도 있습니다. 우리는 코드를 한 줄도 고치지 않았는데 불러오던 외부 스크립트가 변조되면 그 순간 우리 페이지에서 남의 코드가 돕니다. 결제 페이지에서 이런 방식으로 카드 정보가 빠져나간 사고가 여러 차례 있었고, CSP 로 허용 출처를 좁혀 두는 것이 표준적인 대응입니다.

03CHECK IT YOURSELF

직접 확인하는 방법

아래는 저희를 거치지 않고 지금 해 보실 수 있는 방법입니다. 결과를 저희에게 보내실 필요도 없습니다.

  1. 01
    응답 헤더에 있는지 본다

    두 가지 이름을 함께 확인하십시오. Content-Security-Policy 는 실제로 차단하는 것이고, 뒤에 Report-Only 가 붙은 것은 관찰만 하고 차단하지 않습니다. 후자만 있으면 아직 방어가 켜진 상태가 아닙니다.

    $ curl -sI https://example.com/ | grep -i content-security
    content-security-policy-report-only: default-src 'self'; ...
  2. 02
    브라우저 콘솔에서 위반을 본다

    개발자도구를 열고 페이지를 새로고침하면 차단되거나 위반으로 기록된 항목이 콘솔에 남습니다. 정책을 처음 적을 때 무엇이 걸리는지 가장 빨리 보는 방법입니다.

  3. 03
    외부에서 불러오는 것을 먼저 세어 본다

    정책을 적기 전에 이 페이지가 어느 도메인에서 무엇을 받아 오는지 알아야 합니다. 개발자도구 네트워크 탭에서 도메인 기준으로 정렬하면 목록이 나오고, 그 목록이 곧 허용 출처의 초안이 됩니다.

04HOW TO FIX

고치는 방법

서버 환경에 따라 적을 자리가 다릅니다. 아래는 가장 흔한 구성 기준이고, 적용 전에 현재 설정을 백업하십시오.

먼저 Report-Only 로 며칠 관찰한다

이 이름으로 내보내면 브라우저는 아무것도 차단하지 않고 위반만 기록합니다. 실제 방문자들이 쓰는 화면에서 무엇이 걸리는지 모으는 것이 첫 단계입니다. 처음부터 강제하면 멀쩡하던 버튼이 조용히 죽습니다.

Header always set Content-Security-Policy-Report-Only "default-src 'self'; \
  script-src 'self' 'unsafe-inline'; img-src 'self' data: https:; \
  frame-ancestors 'self'; object-src 'none'"
위반 목록을 보고 허용 출처를 좁힌다

기록된 위반 가운데 정상 기능은 출처를 열어 주고, 정체를 모르는 것은 그대로 둡니다. 이 과정에서 아무도 기억하지 못하는 옛 스크립트가 발견되는 일이 자주 있습니다.

충분히 조용해지면 헤더 이름을 바꿔 강제로 전환한다

관찰 기간 동안 정상 기능의 위반이 나오지 않으면 Report-Only 를 떼고 같은 정책을 강제로 적용합니다. 이때 비로소 차단이 시작됩니다.

Header always set Content-Security-Policy "default-src 'self'; \
  script-src 'self'; img-src 'self' data: https:; \
  frame-ancestors 'self'; base-uri 'self'; object-src 'none'"
인라인 스크립트는 나중에 없앤다

unsafe-inline 이 들어 있으면 XSS 방어 효과가 크게 줄어듭니다. 다만 이것을 빼려면 페이지 안에 직접 적힌 스크립트를 전부 파일로 옮기거나 nonce 를 붙여야 해서, 정책을 켜는 것과는 별개의 작업으로 잡는 편이 현실적입니다.

주의. 로그인해야 보이는 화면은 자동 점검에 잡히지 않습니다. 관리자 페이지, 주문·결제 흐름, 게시판 에디터가 대표적이고, 이런 화면의 위반은 200 응답 안에서 버튼이 조용히 죽는 형태로 나타나 스모크 테스트에도 걸리지 않습니다. 강제로 전환하기 전에 로그인 상태로 직접 눌러 보십시오. 저희 사이트도 같은 이유로 지금 Report-Only 로만 두고 있습니다.

05FAQ

자주 묻는 질문

CSP 를 켜면 구글 애널리틱스 같은 것이 막히나요

허용 출처에 적지 않으면 막힙니다. 그래서 Report-Only 관찰이 필요합니다. 며칠 돌려 보면 애널리틱스, 광고, 채팅 위젯, 폰트처럼 실제로 쓰는 외부 자원이 위반 목록에 그대로 나오고, 그 목록을 보고 열어 주면 됩니다.

Report-Only 만 켜 두면 이 항목은 통과되나요

통과되지 않습니다. Report-Only 는 아무것도 차단하지 않기 때문에 방어 효과가 없고, 판정은 실제로 강제되는 헤더가 있는지를 봅니다. 다만 Report-Only 는 켜는 과정의 정상적인 중간 단계이므로, 그 상태 자체가 잘못된 것은 아닙니다.

어떤 정책부터 적는 것이 좋나요

default-src 를 자기 출처로 잡고 object-src 를 막는 것에서 시작하는 것이 무난합니다. 여기에 frame-ancestors 를 더하면 클릭재킹 방어까지 같이 해결됩니다. 처음부터 완벽한 정책을 적으려 하면 켜지 못한 채 몇 달이 지나갑니다.

워드프레스나 그누보드 같은 설치형 CMS 에서도 되나요

됩니다. 다만 관리자 화면과 에디터가 인라인 스크립트를 많이 쓰기 때문에 처음에는 unsafe-inline 을 넣은 채로 시작하게 됩니다. 공개 화면과 관리자 화면에 다른 정책을 적용하는 방법도 있고, 경로별로 헤더를 나눠 내보내면 됩니다.

위반 보고를 어디로 받아야 하나요

보고를 받을 주소를 지정하면 브라우저가 위반 내용을 그리로 보냅니다. 다만 공개된 사이트에서는 잡음이 상당히 많이 들어오기 때문에, 처음에는 보고 수집 없이 개발자도구 콘솔만 보며 정책을 다듬는 편이 오히려 빠릅니다.
여기까지는 항목 일반론입니다. 무엇이고 왜 문제이며 어떻게 고치는지는 감출 이유가 없어 그대로 적었습니다. 리포트에 들어가는 것은 귀사 사이트에서 실제로 어떤 값이 나왔는지 — 어느 주소의 어느 응답인지, 그 환경에서 어떤 순서로 적용해야 하는지, 예상 공수가 얼마인지입니다. 이 페이지만 보고 직접 고치셔도 되고, 실제로 그러시라고 적어 두었습니다.

GET STARTED

내 사이트는
이 항목을 통과할까요

주소만 넣으시면 이 항목을 포함해 25가지를 판정해 점수와 실패 개수를 보여 드립니다. 로그인도, 소유 확인도 필요 없습니다 — 전부 밖에서 관찰만 하는 항목이기 때문입니다.