콘텐츠 보안 정책 : 콘텐츠 보안 정책(CSP)

마이크 웨스트
조 메들리
조 메들리

웹의 보안 모델은 동일 출처 정책을 기반으로 합니다. https://mybank.com의 코드는 https://mybank.com의 데이터에만 액세스할 수 있어야 하고 https://evil.example.com의 액세스는 허용되어서는 안 됩니다. 각 출처는 나머지 웹과 격리되어 개발자에게 빌드 및 플레이를 위한 안전한 샌드박스를 제공합니다. 이론상으로는 완벽하게 훌륭합니다. 실제로는 공격자들이 영리하게 그 시스템을 파괴했습니다.

예를 들어 교차 사이트 스크립팅 (XSS) 공격은 사이트를 속여 의도된 콘텐츠와 함께 악성 코드를 전송하도록 하여 동일한 출처 정책을 우회합니다. 브라우저가 페이지에 표시되는 모든 코드를 페이지의 합법적으로 보안 출처의 일부로 신뢰하기 때문에 이는 매우 심각한 문제입니다. XSS 요약본은 오래되었지만 공격자가 악성 코드를 삽입하여 이러한 신뢰를 위반하는 데 사용할 수 있는 방법의 대표적인 단면입니다. 공격자가 어떤 코드든 성공적으로 삽입하면 거의 게임이 종료됩니다. 사용자 세션 데이터가 유출되고 비공개로 유지되어야 하는 정보가 The Bad Guys에게 유출됩니다. 가능한 한 이러한 일이 발생하지 않도록 해야 합니다.

이 개요에서는 최신 브라우저에서 XSS 공격의 위험과 영향을 크게 줄일 수 있는 방어 수단인 콘텐츠 보안 정책 (CSP)을 중점적으로 설명합니다.

요약

  • 허용 목록을 사용하여 허용되는 항목과 허용되지 않는 항목을 클라이언트에 알립니다.
  • 어떤 지시어를 사용할 수 있는지 알아보세요.
  • 사용하는 키워드 알아보기
  • 인라인 코드와 eval()는 유해한 것으로 간주됩니다.
  • 정책 위반사항을 적용하기 전에 서버에 신고하세요.

소스 허용 목록

XSS 공격에서 악용하는 문제는 브라우저가 애플리케이션에 포함된 스크립트와 서드 파티가 악의적으로 주입한 스크립트를 구별할 수 없다는 것입니다. 예를 들어 이 페이지 하단에 있는 Google +1 버튼은 이 페이지의 출처인 https://apis.google.com/js/plusone.js의 코드를 로드하여 실행합니다. Google은 이 코드를 신뢰하지만 브라우저가 apis.google.com의 코드가 훌륭하다는 것을 브라우저가 스스로 인식할 수는 없지만 apis.evil.example.com의 코드는 그렇지 않을 수 있습니다. 브라우저는 소스에 관계없이 페이지에서 요청하는 모든 코드를 다운로드하여 실행합니다.

CSP는 서버가 제공하는 모든 항목을 맹목적으로 신뢰하는 대신 신뢰할 수 있는 콘텐츠 소스의 허용 목록을 만들 수 있는 Content-Security-Policy HTTP 헤더를 정의하고 브라우저에 이러한 소스의 리소스만 실행하거나 렌더링하도록 지시합니다. 공격자가 스크립트를 삽입할 허점을 찾을 수 있더라도 스크립트가 허용 목록과 일치하지 않으므로 실행되지 않습니다.

Google은 apis.google.com가 유효한 코드를 전달할 것이라고 믿고 있고, Google에서도 그렇게 할 것이라고 믿으므로, 아래의 두 소스 중 하나에서 비롯된 경우에만 스크립트 실행을 허용하는 정책을 정의해 보겠습니다.

Content-Security-Policy: script-src 'self' https://apis.google.com

간단하죠? 짐작하셨겠지만 script-src는 특정 페이지의 스크립트 관련 권한 집합을 제어하는 지시어입니다. 'self'를 하나의 유효한 스크립트 소스로 지정하고 https://apis.google.com를 또 다른 유효한 스크립트 소스로 지정했습니다. 브라우저는 현재 페이지의 출처뿐 아니라 HTTPS를 통해 apis.google.com에서 자바스크립트를 충실히 다운로드하고 실행합니다.

콘솔 오류: 스크립트 'http://evil.example.com/evil.js'가 스크립트 보안 정책 지시어(script-src 'self' https://apis.google.com)를 위반하여 로드가 거부되었습니다.

이 정책을 정의하면 브라우저가 다른 소스에서 스크립트를 로드하는 대신 단순히 오류를 발생시킵니다. 영리한 공격자가 사이트에 코드를 삽입하면 기대했던 성공이 아니라 오류 메시지가 계속 표시됩니다.

다양한 리소스에 적용되는 정책

스크립트 리소스가 가장 명백한 보안 위험이지만, CSP는 페이지에서 로드할 수 있는 리소스를 상당히 세밀하게 제어할 수 있는 다양한 정책 지시어 집합을 제공합니다. script-src는 이미 확인했으므로 개념이 명확해야 합니다.

나머지 리소스 지시문을 빠르게 살펴보겠습니다. 아래 목록은 수준 2의 지시어 상태를 나타냅니다. 수준 3 사양이 게시되었지만 주요 브라우저에서 대체로 구현되지 않았습니다.

  • base-uri는 페이지의 <base> 요소에 표시될 수 있는 URL을 제한합니다.
  • child-src는 작업자 및 삽입된 프레임 콘텐츠의 URL을 나열합니다. 예를 들어 child-src https://youtube.com를 사용하면 YouTube의 동영상을 삽입할 수 있지만 다른 출처의 동영상은 삽입할 수 없습니다.
  • connect-src는 XHR, WebSockets, EventSource를 통해 연결할 수 있는 출처를 제한합니다.
  • font-src은 웹 글꼴을 제공할 수 있는 출처를 지정합니다. Google의 웹 글꼴은 font-src https://themes.googleusercontent.com를 통해 사용 설정할 수 있습니다.
  • form-action에는 <form> 태그에서 제출할 수 있는 유효한 엔드포인트가 나열됩니다.
  • frame-ancestors은 현재 페이지를 삽입할 수 있는 소스를 지정합니다. 이 지시어는 <frame>, <iframe>, <embed>, <applet> 태그에 적용됩니다. 이 지시어는 <meta> 태그에서 사용할 수 없으며 HTML이 아닌 리소스에만 적용됩니다.
  • frame-src은(는) 레벨 2에서 지원 중단되었지만 레벨 3에서 복원되었습니다. 존재하지 않으면 이전과 같이 여전히 child-src로 대체됩니다.
  • img-src는 이미지를 로드할 수 있는 출처를 정의합니다.
  • media-src는 동영상 및 오디오를 전송할 수 있는 출처를 제한합니다.
  • object-src를 사용하면 플래시 및 기타 플러그인을 제어할 수 있습니다.
  • plugin-types는 페이지에서 호출할 수 있는 플러그인의 종류를 제한합니다.
  • report-uri는 콘텐츠 보안 정책을 위반했을 때 브라우저가 보고서를 보낼 URL을 지정합니다. 이 지시어는 <meta> 태그에서 사용할 수 없습니다.
  • style-src는 스타일시트에 대응하는 script-src의 요소입니다.
  • upgrade-insecure-requests는 사용자 에이전트에 URL 스킴을 다시 작성하여 HTTP를 HTTPS로 변경하도록 지시합니다. 이 명령어는 다시 작성해야 하는 이전 URL이 많은 웹사이트에 사용됩니다.
  • worker-src는 작업자, 공유 워커 또는 서비스 워커로 로드될 수 있는 URL을 제한하는 CSP 수준 3 지시어입니다. 2017년 7월 현재, 이 지침은 제한적으로 구현됩니다.

기본적으로 지시어는 광범위하게 공개됩니다. 지시어(예: font-src)에 특정 정책을 설정하지 않으면 이 지시어는 *를 유효한 소스로 지정한 것처럼 기본적으로 작동합니다(예: 제한 없이 어디서나 글꼴을 로드할 수 있음).

default-src 지시어를 지정하여 이 기본 동작을 재정의할 수 있습니다. 이 지시어는 지정되지 않은 상태로 두는 대부분의 지시어의 기본값을 정의합니다. 일반적으로 -src로 끝나는 모든 지시어에 적용됩니다. default-srchttps://example.com로 설정되어 있고 font-src 지시어를 지정하지 못하면 https://example.com에서만 글꼴을 로드할 수 있습니다. 이전 예에서는 script-src만 지정했으므로 이미지, 글꼴 등을 모든 출처에서 로드할 수 있습니다.

다음 지시어는 default-src를 대체 값으로 사용하지 않습니다. 이를 설정하지 않으면 무엇이든 허용하는 것과 같습니다.

  • base-uri
  • form-action
  • frame-ancestors
  • plugin-types
  • report-uri
  • sandbox

지시어를 세미콜론으로 구분하여 HTTP 헤더에 각 지시어를 나열하면 해당 애플리케이션에 적합한 수만큼 이러한 지시어를 사용할 수 있습니다. 특정 유형의 모든 필수 리소스를 단일 지시어에 나열해야 합니다. script-src https://host1.com; script-src https://host2.com와 같이 작성하면 두 번째 지시어가 무시됩니다. 다음과 같은 명령은 두 출처를 모두 유효한 것으로 올바르게 지정합니다.

script-src https://host1.com https://host2.com

예를 들어 콘텐츠 전송 네트워크 (예: https://cdn.example.net)에서 모든 리소스를 로드하는 애플리케이션이 있고 프레이밍된 콘텐츠나 플러그인이 필요하지 않음을 알고 있는 경우 정책은 다음과 같을 수 있습니다.

Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'

구현 세부정보

X-WebKit-CSPX-Content-Security-Policy 헤더는 웹의 다양한 튜토리얼에서 확인할 수 있습니다. 앞으로 이러한 접두사 헤더는 무시해야 합니다. 최신 브라우저 (IE 제외)에서는 접두사가 없는 Content-Security-Policy 헤더를 지원합니다. 바로 이 헤더를 사용해야 합니다.

사용하는 헤더와 관계없이 정책은 페이지별로 정의됩니다. 즉, 보호받고자 하는 모든 응답과 함께 HTTP 헤더를 전송해야 합니다. 특정 요구사항에 따라 특정 페이지의 정책을 세부적으로 조정할 수 있으므로 상당한 유연성이 제공됩니다. 사이트에 한 페이지 세트에는 +1 버튼이 있는 반면 다른 페이지 세트에는 없을 수 있습니다. 필요할 때만 버튼 코드가 로드되도록 허용할 수 있습니다.

각 지시어의 소스 목록은 유연합니다. 스키마 (data:, https:) 또는 호스트 이름만(해당 호스트의 모든 출처와 일치하는 example.com, 모든 스키마, 모든 포트)부터 정규화된 URI (https://example.com:443: HTTPS만 일치, example.com만, 포트 443만 일치)까지 특정 범위의 소스를 지정할 수 있습니다. 와일드 카드는 허용되지만 스키마, 포트 또는 호스트 이름의 가장 왼쪽 위치에서만 허용됩니다. *://*.example.com:*는 모든 포트에서 스키마를 사용하여 example.com의 모든 하위 도메인 (example.com 자체는 아님)과 일치합니다.

소스 목록에는 다음 4개의 키워드도 허용됩니다.

  • 예상할 수 있듯이 'none'는 아무것도 일치하지 않습니다.
  • 'self'는 현재 출처와 일치하지만 하위 도메인과는 일치하지 않습니다.
  • 'unsafe-inline'은 인라인 자바스크립트 및 CSS를 허용합니다. (이 부분에 대해서는 좀 더 자세히 살펴보겠습니다.)
  • 'unsafe-eval'eval와 같은 텍스트-JavaScript 메커니즘을 허용합니다. (이에 대해서도 살펴보겠습니다.)

이러한 키워드에는 작은따옴표가 필요합니다. 예를 들어 script-src 'self'(따옴표 포함)는 현재 호스트에서 JavaScript 실행을 승인합니다. script-src self(따옴표 없음)는 현재 호스트가 아닌 'self'라는 서버의 JavaScript를 허용합니다. 이는 의도와 다를 수 있습니다.

샌드박스 기능

언급할 가치가 있는 명령어가 하나 더 있습니다. sandbox입니다. 이 API는 페이지에서 로드할 수 있는 리소스가 아닌 페이지에서 취할 수 있는 작업에 제한을 두므로 지금까지 살펴본 다른 방법과는 약간 다릅니다. sandbox 지시어가 있으면 페이지는 sandbox 속성이 있는 <iframe> 내부에 로드된 것처럼 처리됩니다. 이는 페이지에 광범위한 영향을 미칠 수 있습니다. 무엇보다도 페이지를 고유한 출처로 강제 적용하고 양식 제출을 방지하는 등 이 도움말의 범위를 조금 벗어나지만 HTML5 사양의 '샌드박스' 섹션에서 유효한 샌드박스 속성에 관한 자세한 내용을 확인할 수 있습니다.

메타 태그

CSP에서 선호하는 전송 메커니즘은 HTTP 헤더입니다. 그러나 마크업에서 직접 페이지의 정책을 설정하는 것이 유용할 수 있습니다. http-equiv 속성이 있는 <meta> 태그를 사용하면 됩니다.

<meta
  http-equiv="Content-Security-Policy"
  content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'"
/>

frame-ancestors, report-uri 또는 sandbox에 사용할 수 없습니다.

인라인 코드가 유해한 것으로 간주됨

CSP는 허용 목록 출처에 기반한다는 점이 분명해야 합니다. 이는 브라우저에게 특정 리소스 세트를 허용 가능한 것으로 처리하고 나머지는 거부하도록 지시하는 명확한 방법이기 때문입니다. 그러나 출처 기반 허용 목록으로 XSS 공격이 제기하는 가장 큰 위협인 인라인 스크립트 삽입을 해결하지는 못합니다. 공격자가 악성 페이로드 (<script>sendMyDataToEvilDotCom();</script>)가 직접 포함된 스크립트 태그를 삽입할 수 있는 경우 브라우저에 이를 합법적인 인라인 스크립트 태그와 구분할 수 있는 메커니즘이 없습니다. CSP는 인라인 스크립트를 완전히 금지하여 이 문제를 해결합니다. 그것이 확실한 유일한 방법입니다.

이러한 차단에는 script 태그에 직접 삽입된 스크립트뿐만 아니라 인라인 이벤트 핸들러와 javascript: URL도 포함됩니다. script 태그의 콘텐츠를 외부 파일로 이동하고 javascript: URL과 <a ... onclick="[JAVASCRIPT]">를 적절한 addEventListener() 호출로 바꿔야 합니다. 예를 들어 다음에서 다시 작성할 수 있습니다.

<script>
  function doAmazingThings() {
    alert('YOU AM AMAZING!');
  }
</script>
<button onclick="doAmazingThings();">Am I amazing?</button>

다음과 같이 변경할 수 있습니다.

<!-- amazing.html -->
<script src="amazing.js"></script>
<button id="amazing">Am I amazing?</button>

<div style="clear:both;"></div>
// amazing.js
function doAmazingThings() {
  alert('YOU AM AMAZING!');
}
document.addEventListener('DOMContentLoaded', function () {
  document.getElementById('amazing').addEventListener('click', doAmazingThings);
});

다시 작성된 코드는 CSP와 잘 작동하는 것 외에 CSP와 함께 잘 작동하는 것 이상의 여러 이점이 있습니다. CSP 사용과 관계없이 이미 권장사항입니다. 인라인 JavaScript는 허용되지 않는 방식으로 구조와 동작을 정확하게 혼합합니다. 외부 리소스는 브라우저가 캐시하기 쉽고 개발자가 더 이해하기 쉬우며 컴파일 및 압축에 도움이 됩니다. 코드를 외부 리소스로 이동하는 작업을 수행하면 더 나은 코드를 작성할 수 있습니다.

인라인 스타일은 동일한 방식으로 처리됩니다. 즉, CSS에서 지원하는 놀라울 정도로 영리한 데이터 무단 반출 메서드로부터 보호하기 위해 style 속성과 style 태그를 모두 외부 스타일시트로 통합해야 합니다.

인라인 스크립트와 스타일이 있어야 하는 경우 'unsafe-inline'script-src 또는 style-src 지시어에서 허용되는 소스로 추가하여 인라인 스크립트와 스타일을 사용 설정할 수 있습니다. nonce나 해시를 사용할 수도 있지만 (아래 참조) 실제로는 사용하면 안 됩니다. 인라인 스크립트를 금지하는 것은 CSP가 제공하는 가장 큰 보안상 이점이며, 인라인 스타일을 금지하면 애플리케이션이 강화됩니다. 모든 코드를 줄 밖으로 이동한 후 올바르게 작동하도록 하기 위해 사전에 약간의 노력이 필요하지만, 그만큼의 가치 있는 절충점입니다.

꼭 사용해야 한다면

CSP 수준 2는 암호화 nonce (한 번 사용되는 숫자) 또는 해시를 사용하여 특정 인라인 스크립트를 허용 목록에 추가할 수 있도록 하여 인라인 스크립트의 이전 버전과의 호환성을 제공합니다. 이는 번거로울 수 있지만, 아주 빠르게 사용할 수 있습니다.

nonce를 사용하려면 스크립트 태그에 nonce 속성을 지정하세요. 값은 신뢰할 수 있는 소스 목록에 있는 값과 일치해야 합니다. 예를 들면 다음과 같습니다.

<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
  // Some inline code I can't remove yet, but need to asap.
</script>

이제 nonce- 키워드에 추가된 script-src 지시어에 nonce를 추가합니다.

Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

모든 페이지 요청에 대해 nonce는 다시 생성해야 하며 추측할 수 없어야 합니다.

해시도 거의 같은 방식으로 작동합니다. 스크립트 태그에 코드를 추가하는 대신 스크립트 자체의 SHA 해시를 만들어 script-src 지시어에 추가합니다. 예를 들어 페이지에 다음이 포함되어 있다고 가정해 보겠습니다.

<script>
  alert('Hello, world.');
</script>

정책에는 다음이 포함됩니다.

Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

여기서 몇 가지 주의할 사항이 있습니다. sha*- 프리픽스는 해시를 생성하는 알고리즘을 지정합니다. 위의 예에서는 sha256-이 사용됩니다. CSP는 sha384-sha512-도 지원합니다. 해시를 생성할 때 <script> 태그를 포함하지 마세요. 또한 대소문자와 선행 또는 후행 공백을 비롯한 공백도 중요합니다.

SHA 해시 생성에 관해 Google에서 검색하면 여러 언어로 제공되는 솔루션을 찾을 수 있습니다. Chrome 40 이상을 사용하는 경우 DevTools를 연 후 페이지를 새로고침할 수 있습니다. 콘솔 탭에는 각 인라인 스크립트에 올바른 sha256 해시가 포함된 오류 메시지가 포함됩니다.

평가도

공격자가 스크립트를 직접 삽입할 수 없더라도 애플리케이션을 속여서 다른 비활성 텍스트를 실행 가능한 자바스크립트로 변환하여 실행하도록 유도할 수 있습니다. eval(), 새 Function() , setTimeout([string], ...), setInterval([string], ...)는 모두 삽입된 텍스트가 예기치 않게 악의적인 것을 실행할 수 있는 벡터입니다. 이 위험에 대한 CSP의 기본 응답은 이러한 벡터를 모두 완전히 차단하는 것입니다.

이는 애플리케이션을 빌드하는 방식에 몇 가지 영향을 미칩니다.

  • eval에 의존하지 않고 기본 제공 JSON.parse를 통해 JSON을 파싱해야 합니다. 기본 JSON 작업은 IE8 이후의 모든 브라우저에서 사용할 수 있으며 완전히 안전합니다.
  • 문자열이 아닌 인라인 함수를 사용하여 현재 실행하고 있는 setTimeout 또는 setInterval 호출을 다시 작성합니다. 예를 들면 다음과 같습니다.
setTimeout("document.querySelector('a').style.display = 'none';", 10);

다음과 같이 작성하는 것이 좋습니다.

setTimeout(function () {
  document.querySelector('a').style.display = 'none';
}, 10);
  • 런타임 시 인라인 템플릿 방지: 많은 템플릿 라이브러리가 new Function()를 자유롭게 사용하여 런타임 시 템플릿 생성 속도를 높입니다. 이는 멋진 동적 프로그래밍 적용 사례이지만 악성 텍스트를 평가할 위험이 있습니다. 일부 프레임워크는 즉시 CSP를 지원하여 eval가 없으면 강력한 파서로 대체합니다. AngularJS의 ng-csp 지시어가 이에 대한 좋은 예입니다.

하지만 사전 컴파일을 제공하는 템플릿 언어를 사용하는 것이 더 좋습니다 (예: Handlebars 지원). 템플릿을 사전 컴파일하면 사용자 환경이 가장 빠른 런타임 구현보다 훨씬 더 빠르며 더 안전합니다. eval 및 해당 텍스트-자바스크립트 형제가 애플리케이션에 필수적인 경우 script-src 지시어에서 'unsafe-eval'를 허용되는 소스로 추가하여 이를 사용 설정할 수 있지만 권장하지 않습니다. 문자열을 실행하는 기능을 차단하면 공격자가 사이트에서 승인되지 않은 코드를 실행하기가 훨씬 더 어려워집니다.

보고서

클라이언트 측에서 신뢰할 수 없는 리소스를 차단하는 CSP의 기능은 사용자에게 큰 도움이 되지만, 일종의 알림이 서버로 다시 전송되도록 하여 악의적인 삽입을 허용하는 버그를 애초에 식별하고 박멸하는 것이 유용할 수 있습니다. 이를 위해 report-uri 지시어에 지정된 위치에 JSON 형식 위반 신고를 POST하도록 브라우저에 지시할 수 있습니다.

Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

이러한 보고서는 다음과 같이 표시됩니다.

{
  "csp-report": {
    "document-uri": "http://example.org/page.html",
    "referrer": "http://evil.example.com/",
    "blocked-uri": "http://evil.example.com/evil.js",
    "violated-directive": "script-src 'self' https://apis.google.com",
    "original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
  }
}

여기에는 위반이 발생한 페이지 (document-uri), 해당 페이지의 리퍼러 (HTTP 헤더 필드와 달리 키는 맞춤법이 틀리지 않음), 페이지의 정책을 위반한 리소스 (blocked-uri), 위반한 특정 지시어(violated-directive), 페이지의 전체 정책 (original-policy) 등 위반의 구체적인 원인을 추적하는 데 도움이 되는 양질의 정보가 포함되어 있습니다.

보고서 전용

CSP를 이제 막 시작한 경우 사용자에게 엄격한 정책을 적용하기 전에 애플리케이션의 현재 상태를 평가하는 것이 좋습니다. 배포를 완료하기 위한 디딤돌로서 브라우저에 정책을 모니터링하도록 요청하여 위반을 보고하지만 제한을 적용하지는 않을 수 있습니다. Content-Security-Policy 헤더를 전송하는 대신 Content-Security-Policy-Report-Only 헤더를 전송하세요.

Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

보고서 전용 모드에 지정된 정책은 제한된 리소스를 차단하지 않지만 지정된 위치에 위반 보고서를 전송합니다. 헤더를 모두 전송하여 한 정책을 적용하는 동시에 다른 정책을 모니터링할 수도 있습니다. 이는 애플리케이션의 CSP 변경사항이 미치는 영향을 평가하는 좋은 방법입니다. 새 정책의 보고를 사용 설정하고 위반 신고를 모니터링하고 발생하는 버그를 수정하세요. 효과가 만족스러우면 새 정책을 시행하세요.

실제 사용

CSP 1은 Chrome, Safari, Firefox에서 꽤 유용하지만 IE 10에서는 매우 제한적으로 지원됩니다. 자세한 내용은 caniuse.com에서 확인할 수 있습니다. CSP 수준 2는 버전 40부터 Chrome에서 제공되었습니다. Twitter, Facebook과 같은 대규모 사이트에서 헤더를 배포했으며(Twitter의 사례 연구를 읽어볼 만함) 자체 사이트에 이 표준을 배포할 준비가 되어 있습니다.

애플리케이션 정책을 만드는 첫 번째 단계는 실제로 로드하는 리소스를 평가하는 것입니다. 앱에 요소를 통합하는 방법을 결정했다면 이러한 요구사항을 기반으로 정책을 설정하세요. 몇 가지 일반적인 사용 사례를 살펴보고 CSP의 보호 범위 내에서 이를 가장 효과적으로 지원하는 방법을 결정해 보겠습니다.

사용 사례 1: 소셜 미디어 위젯

  • Google의 +1 버튼에는 https://apis.google.com의 스크립트가 포함되어 있으며 https://plusone.google.com<iframe>가 삽입됩니다. 버튼을 삽입하려면 이러한 두 출처를 모두 포함하는 정책이 필요합니다. 최소 정책은 script-src https://apis.google.com; child-src https://plusone.google.com입니다. 또한 Google에서 제공하는 자바스크립트 스니펫을 외부 자바스크립트 파일로 가져와야 합니다. frame-src를 사용하는 수준 1 기반 정책의 경우 수준 2에서 child-src로 변경해야 했습니다. CSP 수준 3에서는 더 이상 필요하지 않습니다.

  • Facebook의 좋아요 버튼에는 여러 가지 구현 옵션이 있습니다. <iframe> 버전이 사이트의 나머지 부분에서 안전하게 샌드박스 처리되므로 이 버전을 유지하는 것이 좋습니다. 제대로 작동하려면 child-src https://facebook.com 지시어가 필요합니다. 기본적으로 Facebook에서 제공하는 <iframe> 코드는 상대 URL인 //facebook.com을 로드합니다. HTTPS를 명시적으로 지정하려면 다음과 같이 변경합니다. https://facebook.com 꼭 그럴 필요가 없다면 HTTP를 사용할 이유가 없습니다.

  • 트위터의 트윗 버튼은 스크립트와 프레임에 대한 액세스를 기반으로 하며, 둘 다 https://platform.twitter.com에서 호스팅됩니다. 마찬가지로 Twitter는 상대 URL을 기본적으로 제공합니다. 로컬에서 복사/붙여넣을 때 HTTPS를 지정하도록 코드를 수정합니다. Twitter에서 제공하는 자바스크립트 스니펫을 외부 자바스크립트 파일로 이동하기만 하면 script-src https://platform.twitter.com; child-src https://platform.twitter.com로 모두 설정됩니다.

  • 다른 플랫폼에도 유사한 요구사항이 있으며 비슷하게 해결할 수 있습니다. default-src'none'로 설정하고 콘솔을 확인하여 위젯이 작동하도록 하기 위해 사용 설정해야 하는 리소스를 확인하는 것이 좋습니다.

여러 위젯을 포함하는 방법은 간단합니다. 단일 유형의 모든 리소스를 단일 지시어로 병합한다는 점을 기억하고 정책 지시어를 결합하기만 하면 됩니다. 세 가지 소셜 미디어 위젯을 모두 사용하려는 경우 정책은 다음과 같습니다.

script-src https://apis.google.com https://platform.twitter.com; child-src https://plusone.google.com https://facebook.com https://platform.twitter.com

사용 사례 #2: 잠금

은행 사이트를 운영하면서 직접 작성한 리소스만 로드할 수 있는지 확인하려 한다고 가정해 보겠습니다. 이 시나리오에서는 완전히 모든 항목 (default-src 'none')을 차단하는 기본 정책으로 시작하여 이를 바탕으로 구축합니다.

은행이 https://cdn.mybank.net의 CDN에서 모든 이미지, 스타일, 스크립트를 로드하고 XHR을 통해 https://api.mybank.com/에 연결하여 다양한 데이터 비트를 다운로드한다고 가정해 보겠습니다. 프레임이 사용되지만 사이트에 로컬인 페이지에만 사용됩니다 (서드 파티 출처 없음). 이 사이트에는 플래시도, 글꼴도, 추가 기능도 전혀 없습니다. 전송 가능한 가장 제한적인 CSP 헤더는 다음과 같습니다.

Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; child-src 'self'

사용 사례 #3: SSL만

한 결혼반지 토론 포럼 관리자는 모든 리소스가 보안 채널을 통해서만 로드되도록 하려고 하지만 실제로 코드를 많이 작성하지는 않습니다. 인라인 스크립트와 스타일로 둘러싸여 있는 타사 포럼 소프트웨어의 많은 부분을 재작성하는 것은 할 수 없는 일입니다. 다음 정책이 효과적입니다.

Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'

https:default-src에 지정되더라도 스크립트와 스타일 지시어는 해당 소스를 자동으로 상속하지 않습니다. 각 지시어는 특정 리소스 유형의 기본값을 완전히 덮어씁니다.

앞으로

콘텐츠 보안 정책 수준 2는 후보 추천입니다. W3C의 웹 애플리케이션 보안 실무 그룹은 이미 사양의 다음 버전인 콘텐츠 보안 정책 수준 3 작업을 시작했습니다.

이러한 출시 예정 기능에 대한 토론에 관심이 있다면 public-webappsec@ 메일링 리스트 아카이브를 훑어보거나 직접 참여하세요.

의견