Content Security Policy와 CMP: CSP와 동의를 함께 작동시키기
서로 일치해야 하는 두 가지 보안 계층
Content Security Policy(CSP)는 페이지가 스크립트, 스타일, 이미지, 프레임, 네트워크 요청을 어떤 출처에서 불러올 수 있는지 브라우저에 알려 줍니다. 동의 관리 플랫폼은 반대의 일을 합니다. 사용자의 선택에 따라 그 스크립트 중 어떤 것을 실행할 수 있는지 결정합니다. 둘을 따로 구성하면 충돌합니다. CSP가 막아서 CMP가 로드되지 않거나, CMP와 광고 태그를 수용하려고 CSP를 지나치게 느슨하게 만들어 더 이상 아무것도 보호하지 못하게 됩니다.
이 가이드에서는 CMP가 일반적으로 필요로 하는 CSP 지시문, nonce가 스크립트 주입과 어떻게 상호작용하는지, 배너를 망가뜨리지 않고 변경 사항을 적용하는 방법을 설명합니다.
CMP가 일반적으로 CSP에 요구하는 것
정확한 요구 사항은 벤더에 따라 다르므로 항상 CMP 자체 문서에서 시작하십시오. 일반적으로 다음 지시문을 확인합니다.
script-src— CMP 스크립트가 로드되는 도메인과 CMP가 로드하는 추가 스크립트 파일.connect-src— CMP가fetch또는 XHR로 호출하는 엔드포인트(구성, 벤더 목록, 지리 위치, 동의 로깅 엔드포인트 등).style-src— 스타일시트 도메인, 그리고 CMP가<style>요소를 삽입하거나 style 속성을 설정하는 경우 인라인 스타일.img-src— CMP가 사용하는 로고, 아이콘, 추적 기능이 없는 픽셀.font-src— 배너가 자체 글꼴을 로드하는 경우.frame-src— CMP가 iframe을 사용하는 경우(예: 도메인 간 동의 공유).
TCF 사이트에서는 CMP가 페이지에 __tcfapiLocator라는 이름의 로케이터 프레임도 만든다는 점을 기억하십시오. 이 프레임은 원격 소스 없이 생성되므로 보통 frame-src 항목이 필요하지 않지만, 테스트에서 확인해 볼 가치가 있습니다.
인라인 스텁 문제
대부분의 CMP는 메인 CMP가 로드되기 전에 다른 스크립트가 호출을 큐에 넣을 수 있도록, 페이지 head에 작은 인라인 스크립트인 스텁을 두라고 요구합니다. 엄격한 CSP는 명시적으로 허용하지 않는 한 인라인 스크립트를 차단합니다. 선택지는 세 가지입니다.
- nonce. 응답마다 임의의 값을 생성하여 CSP 헤더에
'nonce-...'로, 그리고 스텁의<script>태그에 추가합니다. 동적 페이지에 가장 유연한 옵션입니다. - 해시. 스텁의 정확한 내용에 대한 SHA-256 해시를
script-src에 추가합니다. 정적 사이트에서는 잘 작동하지만, 공백 하나라도 스텁을 변경하면 동작하지 않습니다. - 스텁 자체 호스팅. 자체 도메인의 외부 파일로 두고
'self'로 허용합니다. 충분히 일찍 로드되도록 주의해야 합니다.
지름길로 'unsafe-inline'을 쓰는 것은 피하십시오. 모든 인라인 스크립트를 허용하여 CSP가 제공하는 보호의 상당 부분을 없애 버립니다.
nonce와 CMP가 주입하는 스크립트
동의 전까지 스크립트를 차단하는 CMP는 흔히 스크립트를 보류했다가 나중에 주입하는 방식으로 작동합니다. nonce 기반 CSP에서는 이렇게 주입된 스크립트도 허용되어야 합니다. 일반적인 접근 방식은 두 가지입니다.
- nonce 전파. CMP가 자신의 스크립트 태그에서 nonce를 읽어 주입하는 각 스크립트에 추가합니다. 사용 중인 CMP가 이를 지원하는지 확인하십시오.
'strict-dynamic'. 이 키워드는 지원하는 브라우저에게 이미 신뢰된 스크립트가 로드한 스크립트를 신뢰하라고 알려 줍니다. 스크립트를 주입하는 태그 관리자 및 CMP와 함께 쓰기가 편해지지만, 신뢰한 어떤 스크립트든 다른 스크립트를 로드할 수 있다는 의미이기도 하므로 신중하게 신뢰하십시오.
태그 관리자도 같은 문제를 제기합니다. 광고 및 분석 태그를 태그 관리자를 통해 로드한다면 nonce에 관한 해당 문서를 따르고, CMP와 태그 관리자가 일관된 방식을 사용하도록 하십시오.
광고 태그가 CSP를 더 어렵게 만든다
프로그래매틱 광고는 많은 도메인에서 스크립트와 프레임을 로드하며, 수요 파트너가 바뀜에 따라 그 목록도 바뀝니다. 퍼블리셔는 보통 둘 중 하나를 선택합니다.
- 프로그래매틱 광고가 없는 페이지에는 엄격한 CSP를, 광고가 실행되는 곳에는 더 넓은 정책을 적용합니다.
frame-ancestors,object-src 'none',base-uri처럼 관리 가능한 지시문에 초점을 맞춘 정책을 쓰고, 스크립트 소스는 더 넓게 둡니다.
어느 쪽이든 CMP는 와일드카드로만 포함되는 것이 아니라 명시적인 허용 목록에 있어야 하며, 그래야 CSP 변경이 배너를 조용히 비활성화하지 않습니다.
Report-Only 모드로 적용하기
먼저 Content-Security-Policy-Report-Only 헤더를 사용하십시오. 브라우저는 아무것도 차단하지 않고 위반을 보고하므로, 새 정책이 무엇을 망가뜨릴지 확인할 수 있습니다.
- 보고 엔드포인트와 함께 정책을 report-only 모드로 배포합니다.
- 깨끗한 브라우저 프로필에서 페이지를 로드하고 첫 방문, 모두 수락, 모두 거부, 사용자 지정 선택, 철회까지 전체 동의 흐름을 거칩니다.
- 위반 보고서와 브라우저 콘솔에서 차단된 CMP 리소스를 검토합니다.
- 정책을 조정한 다음 강제 모드로 전환합니다.
- 강제 적용 후에도 보고를 계속 켜 두어 회귀를 포착합니다.
CSP 충돌의 증상
- 배너가 전혀 나타나지 않고, 콘솔에 CMP 스크립트에 대한 CSP 위반이 표시된다.
- 배너는 나타나지만 스타일이 없다.
style-src가 CSS를 차단하기 때문이다. - 선택이 저장되지 않는다.
connect-src가 동의 로깅 호출을 차단하기 때문이다. - 동의 후 광고나 분석이 로드되지 않는다. 주입된 스크립트에 nonce가 없기 때문이다.
__tcfapi가 정의되지 않는다. 인라인 스텁이 차단되었기 때문이다.
핵심 요약
- CMP를 위해
script-src,connect-src,style-src,img-src,frame-src를 확인하십시오. - 인라인 스텁은
'unsafe-inline'이 아니라 nonce 또는 해시로 허용하십시오. - 동의 후 주입되는 스크립트가 nonce를 받거나
'strict-dynamic'으로 포함되도록 하십시오. - 강제하기 전에 report-only 모드에서 모든 동의 경로를 테스트하십시오.
- CSP 변경으로 CMP가 비활성화되지 않도록 CMP를 명시적으로 허용 목록에 유지하십시오.