콘텐츠 보안 정책

콘텐츠 보안 정책이란?

XSS 공격에 대한 최선의 보호 중 하나는 사이트에 콘텐츠 보안 정책(CSP)을 구현하는 것입니다. 이를 위해서는 이미지, 스타일시트, JavaScript 파일 등을 포함하여 사이트의 HTML에 포함된 각 콘텐츠 소스를 지정하고 승인해야 합니다. 브라우저는 명시적으로 승인되지 않은 소스의 콘텐츠를 거부합니다. 이 인증은 응답의 Content-Security-Policy 헤더 내에 정의되며 다양한 구성 옵션을 제공합니다.

이것은 복잡해 보이며 일부 사이트에서는 확실히 어려울 수 있습니다. 그러나 모든 콘텐츠가 동일한 도메인(예: http://example.com)에서 제공되는 많은 간단한 사이트의 경우 통합이 매우 간단합니다.

이는 복잡한 주제이므로 이 사용자 가이드에서는 모든 세부 사항을 다루지는 않습니다. 자세한 내용을 보려면 다음 사이트를 방문해야 합니다.

CSP 켜기

중요

Debug Toolbar은 인라인 스크립트를 출력하는 Kint를 사용할 수 있습니다. 따라서 CSP를 켜면 디버그 도구 모음에 CSP nonce가 자동으로 출력됩니다. 그러나 CSP nonce를 사용하지 않는 경우 CSP 헤더가 의도하지 않은 것으로 변경되어 프로덕션 환경과 다르게 동작합니다. CSP 동작을 확인하려면 디버그 도구 모음을 끄세요.

기본적으로 이에 대한 지원은 꺼져 있습니다. 애플리케이션에서 지원을 활성화하려면 app/Config/App.php에서 CSPEnabled 값을 편집하세요.

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class App extends BaseConfig
{
    // ...

    public bool $CSPEnabled = true;
}

활성화되면 응답 개체에 CodeIgniter\HTTP\ContentSecurityPolicy 인스턴스가 포함됩니다. app/Config/ContentSecurityPolicy.php에 설정된 값이 해당 인스턴스에 적용되며, 런타임 중에 변경이 필요하지 않으면 올바른 형식의 헤더가 전송되고 모든 작업이 완료됩니다.

CSP가 활성화되면 HTTP 응답에 두 개의 헤더 라인이 추가됩니다. 하나는 다양한 컨텍스트에 명시적으로 허용되는 콘텐츠 유형이나 원본을 식별하는 정책이 포함된 Content-Security-Policy 헤더이고, 다른 하나는 허용되지만 선택한 대상에 보고될 콘텐츠 유형이나 원본을 식별하는 Content-Security-Policy-Report-Only 헤더입니다.

우리의 구현은 reportOnly() 메서드를 통해 변경 가능한 기본 처리를 제공합니다. CSP 지시문에 추가 항목이 추가되면 아래와 같이 차단 또는 방지에 적합한 CSP 헤더에 추가됩니다. 추가 메소드 호출에 선택적 두 번째 매개변수를 제공하여 호출별로 재정의할 수 있습니다.

런타임 구성

애플리케이션이 런타임에 변경해야 하는 경우 컨트롤러의 $this->response->getCSP()에서 인스턴스에 접근할 수 있습니다.

클래스에는 설정해야 하는 적절한 헤더 값에 매우 명확하게 매핑되는 여러 메서드가 있습니다. 아래에는 다양한 매개변수 조합이 포함된 예가 나와 있습니다. 단, 모두 지시어 이름이나 배열을 허용합니다.

<?php

// get the CSP instance
$csp = $this->response->getCSP();

// specify the default directive treatment
$csp->reportOnly(false);

// specify the origin to use if none provided for a directive
$csp->setDefaultSrc('cdn.example.com');

// specify the URL that "report-only" reports get sent to
$csp->setReportURI('http://example.com/csp/reports');

// specify that HTTP requests be upgraded to HTTPS
$csp->upgradeInsecureRequests(true);

// add types or origins to CSP directives
// assuming that the default treatment is to block rather than just report
$csp->addBaseURI('example.com', true); // report only
$csp->addChildSrc('https://youtube.com'); // blocked
$csp->addConnectSrc('https://*.facebook.com', false); // blocked
$csp->addFontSrc('fonts.example.com');
$csp->addFormAction('self');
$csp->addFrameAncestor('none', true); // report this one
$csp->addImageSrc('cdn.example.com');
$csp->addMediaSrc('cdn.example.com');
$csp->addManifestSrc('cdn.example.com');
$csp->addObjectSrc('cdn.example.com', false); // reject from here
$csp->addPluginType('application/pdf', false); // reject this media type
$csp->addScriptSrc('scripts.example.com', true); // allow but report requests from here
$csp->addStyleSrc('css.example.com');
$csp->addSandbox(['allow-forms', 'allow-scripts']);

// the following CSP3 directives are available in v4.7.0 and later
$csp->addScriptSrcAttr('trusted.com');
$csp->addScriptSrcElem('trusted.com');
$csp->addStyleSrcAttr('trusted.com');
$csp->addStyleSrcElem('trusted.com');
$csp->addWorkerSrc('workers.example.com');

각 “add” 메소드의 첫 번째 매개변수는 적절한 문자열 값 또는 그 배열입니다.

보고 전용

reportOnly() 메소드를 사용하면 재정의되지 않는 한 후속 소스에 대한 기본 보고 처리를 지정할 수 있습니다.

예를 들어 youtube.com이 허용되도록 지정한 다음 허용되지만 보고된 여러 소스를 제공할 수 있습니다.

<?php

// get the CSP instance
$csp = $this->response->getCSP();

$csp->addChildSrc('https://youtube.com'); // allowed
$csp->reportOnly(true);
$csp->addChildSrc('https://metube.com'); // allowed but reported
$csp->addChildSrc('https://ourtube.com', false); // allowed

보고 지시문

보고서를 보낼 URL을 지정하려면 setReportURI() 메소드를 사용할 수 있습니다.

Added in version 4.7.0.

CSP 레벨 3에서는 report-to``을 위해 ``report-uri 지시어를 더 이상 사용하지 않습니다. 따라서 setReportToEndpoint() 메서드를 사용하여 CSP 보고서에 대한 보고 끝점을 설정할 수 있습니다. 이 지시문을 추가하기 전에 보고 엔드포인트가 addReportingEndpoints() 메서드를 사용하여 이미 정의되어 있는지 확인하세요.

<?php

// get the CSP instance
$csp = $this->response->getCSP();

$csp->setReportURI('https://example.com/csp-reports');

// Starting in v4.7.0, you can use the setReportToEndpoint() method
// to set the reporting endpoint for CSP reports
$csp->addReportingEndpoints([
    'default' => 'https://example.com/csp-reports',
    'reports' => 'https://example.com/other-csp-reports',
]);
$csp->setReportToEndpoint('default');

report-to 지시문을 지원하지 않는 브라우저와의 이전 버전과의 호환성을 위해 CodeIgniter4는 setReportToEndpoint() 메서드를 사용할 때 report-uri 지시문도 설정합니다.

지시문 지우기

기존 CSP 지시문을 지우려면 clearDirective() 메서드를 사용할 수 있습니다.

<?php

// get the CSP instance
$csp = $this->response->getCSP();

$csp->clearDirective('style-src');

인라인 콘텐츠

사용자 생성 콘텐츠의 결과일 수 있으므로 자체 페이지에 있는 인라인 스크립트와 스타일도 보호하지 않도록 웹사이트를 설정할 수 있습니다. 이를 방지하기 위해 CSP에서는 <style><script> 태그 내에 nonce를 지정하고 해당 값을 응답 헤더에 추가할 수 있습니다.

플레이스홀더 사용

이는 실제 생활에서 처리하기 어려운 작업이며 즉시 생성될 때 가장 안전합니다. 이를 간단하게 만들기 위해 태그에 {csp-style-nonce} 또는 {csp-script-nonce} 자리 표시자를 포함하면 자동으로 처리됩니다.

// Original
<script {csp-script-nonce}>
    console.log("Script won't run as it doesn't contain a nonce attribute");
</script>

// Becomes
<script nonce="Eskdikejidojdk978Ad8jf">
    console.log("Script won't run as it doesn't contain a nonce attribute");
</script>

// OR
<style {csp-style-nonce}>
    . . .
</style>

경고

공격자가 <script {csp-script-nonce}>``과 같은 문자열을 삽입하면 기능을 사용하여 실제 nonce 속성이 있습니다. **app/Config/ContentSecurityPolicy.php**에서 ``$scriptNonceTag$styleNonceTag 속성을 사용하여 자리 표시자 문자열을 사용자 정의할 수 있습니다.

함수 사용

위의 자동 교체 기능이 마음에 들지 않으면 app/Config/ContentSecurityPolicy.php에서 $autoNonce = false을 설정하여 기능을 끌 수 있습니다.

이 경우 csp_script_nonce()csp_style_nonce() 함수를 사용할 수 있습니다:

// Original
<script <?= csp_script_nonce() ?>>
    console.log("Script won't run as it doesn't contain a nonce attribute");
</script>

// Becomes
<script nonce="Eskdikejidojdk978Ad8jf">
    console.log("Script won't run as it doesn't contain a nonce attribute");
</script>

// OR
<style <?= csp_style_nonce() ?>>
    . . .
</style>