01DEFINITION
한 줄로 말하면
CSP 는 "이 페이지는 어디서 온 스크립트만 실행한다"를 브라우저에 미리 알려 두는 헤더입니다. 없으면 게시판 글이나 댓글에 스크립트가 한 줄 끼어들었을 때 브라우저가 그것을 그대로 실행합니다. XSS 를 막는 마지막 그물이고, OWASP 2025 판에서 보안 설정 오류가 2위까지 올라온 배경이기도 합니다. 다만 처음부터 강제하면 멀쩡하던 기능이 조용히 죽으므로, 위반만 수집하는 Report-Only 로 며칠 관찰한 뒤 켜는 것이 순서입니다.
02RISK
없으면 무슨 일이 생기나
게시판이나 댓글이 있는 사이트를 생각해 보십시오. 입력값 처리에 구멍이 하나 생겨 누군가 글 안에 스크립트를 심으면, 그 글을 여는 모든 사람의 브라우저에서 그 스크립트가 우리 사이트의 권한으로 실행됩니다. 로그인 세션을 훔쳐 외부로 보내는 데 몇 줄이면 충분합니다. CSP 는 그 스크립트가 실행되기 직전에, 그리고 데이터가 밖으로 나가기 직전에 한 번 더 막습니다.
입력값 검증을 잘하면 되는 것 아니냐고 물으실 수 있습니다. 맞는 말이지만, 사이트에는 우리가 쓰지 않은 코드가 늘 섞여 있습니다. 게시판 스킨, 에디터 플러그인, 광고 스크립트, 채팅 위젯 가운데 하나가 취약하면 우리 검증은 통과된 상태로 사고가 납니다. CSP 가 마지막 그물이라고 불리는 이유가 이것입니다.
외부 스크립트가 바뀌는 경우도 있습니다. 우리는 코드를 한 줄도 고치지 않았는데 불러오던 외부 스크립트가 변조되면 그 순간 우리 페이지에서 남의 코드가 돕니다. 결제 페이지에서 이런 방식으로 카드 정보가 빠져나간 사고가 여러 차례 있었고, CSP 로 허용 출처를 좁혀 두는 것이 표준적인 대응입니다.
03CHECK IT YOURSELF
직접 확인하는 방법
아래는 저희를 거치지 않고 지금 해 보실 수 있는 방법입니다. 결과를 저희에게 보내실 필요도 없습니다.
-
01
응답 헤더에 있는지 본다
두 가지 이름을 함께 확인하십시오. Content-Security-Policy 는 실제로 차단하는 것이고, 뒤에 Report-Only 가 붙은 것은 관찰만 하고 차단하지 않습니다. 후자만 있으면 아직 방어가 켜진 상태가 아닙니다.
$ curl -sI https://example.com/ | grep -i content-security content-security-policy-report-only: default-src 'self'; ...
-
02
브라우저 콘솔에서 위반을 본다
개발자도구를 열고 페이지를 새로고침하면 차단되거나 위반으로 기록된 항목이 콘솔에 남습니다. 정책을 처음 적을 때 무엇이 걸리는지 가장 빨리 보는 방법입니다.
-
03
외부에서 불러오는 것을 먼저 세어 본다
정책을 적기 전에 이 페이지가 어느 도메인에서 무엇을 받아 오는지 알아야 합니다. 개발자도구 네트워크 탭에서 도메인 기준으로 정렬하면 목록이 나오고, 그 목록이 곧 허용 출처의 초안이 됩니다.
04HOW TO FIX
고치는 방법
서버 환경에 따라 적을 자리가 다릅니다. 아래는 가장 흔한 구성 기준이고, 적용 전에 현재 설정을 백업하십시오.
이 이름으로 내보내면 브라우저는 아무것도 차단하지 않고 위반만 기록합니다. 실제 방문자들이 쓰는 화면에서 무엇이 걸리는지 모으는 것이 첫 단계입니다. 처음부터 강제하면 멀쩡하던 버튼이 조용히 죽습니다.
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 를 붙여야 해서, 정책을 켜는 것과는 별개의 작업으로 잡는 편이 현실적입니다.
05FAQ
자주 묻는 질문
CSP 를 켜면 구글 애널리틱스 같은 것이 막히나요
- 허용 출처에 적지 않으면 막힙니다. 그래서 Report-Only 관찰이 필요합니다. 며칠 돌려 보면 애널리틱스, 광고, 채팅 위젯, 폰트처럼 실제로 쓰는 외부 자원이 위반 목록에 그대로 나오고, 그 목록을 보고 열어 주면 됩니다.
Report-Only 만 켜 두면 이 항목은 통과되나요
- 통과되지 않습니다. Report-Only 는 아무것도 차단하지 않기 때문에 방어 효과가 없고, 판정은 실제로 강제되는 헤더가 있는지를 봅니다. 다만 Report-Only 는 켜는 과정의 정상적인 중간 단계이므로, 그 상태 자체가 잘못된 것은 아닙니다.
어떤 정책부터 적는 것이 좋나요
- default-src 를 자기 출처로 잡고 object-src 를 막는 것에서 시작하는 것이 무난합니다. 여기에 frame-ancestors 를 더하면 클릭재킹 방어까지 같이 해결됩니다. 처음부터 완벽한 정책을 적으려 하면 켜지 못한 채 몇 달이 지나갑니다.
워드프레스나 그누보드 같은 설치형 CMS 에서도 되나요
- 됩니다. 다만 관리자 화면과 에디터가 인라인 스크립트를 많이 쓰기 때문에 처음에는 unsafe-inline 을 넣은 채로 시작하게 됩니다. 공개 화면과 관리자 화면에 다른 정책을 적용하는 방법도 있고, 경로별로 헤더를 나눠 내보내면 됩니다.
위반 보고를 어디로 받아야 하나요
- 보고를 받을 주소를 지정하면 브라우저가 위반 내용을 그리로 보냅니다. 다만 공개된 사이트에서는 잡음이 상당히 많이 들어오기 때문에, 처음에는 보고 수집 없이 개발자도구 콘솔만 보며 정책을 다듬는 편이 오히려 빠릅니다.
GET STARTED
내 사이트는
이 항목을 통과할까요
주소만 넣으시면 이 항목을 포함해 25가지를 판정해 점수와 실패 개수를 보여 드립니다. 로그인도, 소유 확인도 필요 없습니다 — 전부 밖에서 관찰만 하는 항목이기 때문입니다.