Utwardzanie infrastruktury internetowej: Kontrola obcych skryptów za pomocą Content Security Policy (CSP)

Management Summary
Content Security Policy, czyli CSP, to reguły witryny określające, jakie źródła i programy może ładować przeglądarka. Służą ograniczaniu niepożądanego kodu i części ataków polegających na wstawianiu szkodliwej treści. Ma to znaczenie zwłaszcza na stronach korzystających z usług wielu zewnętrznych dostawców. Potrzebne funkcje muszą nadal działać, przy ograniczeniu zbędnego dostępu. CSP stanowi dodatkową warstwę ochrony: dozwolony program także może nadużywać danych, więc sama zgoda na źródło nie gwarantuje zaufania.
Przejdź do interaktywnego przykładu ↓
Spis treści
Zakres ochrony CSP
Content Security Policy (CSP) określa zasoby dozwolone do pobrania oraz skrypty dozwolone do wykonania dla dokumentu. Cross-site scripting polega na uruchomieniu wstrzykniętego kodu wewnątrz strony. CSP utrudnia niektóre ataki tego rodzaju. Dopuszczony JavaScript nadal ma jednak dostęp do dokumentu. Sama obecność CSP nie rozpoznaje przejętej biblioteki z dozwolonego źródła.
Escapowanie zamienia znaki specjalne tak, aby tekst nie został odczytany jako kod. Walidacja sprawdza, czy przesłane wartości spełniają oczekiwane reguły. CSP uzupełnia te działania i kontrolę używanych bibliotek zewnętrznych. Lista dozwolonych hostów wskazuje dopuszczone serwery źródłowe. Nie sprawdza, czy każdy pobrany plik jest niezmieniony i godny zaufania. Nie zapewnia też zgody. Złośliwy skrypt nadal może kopiować wpisane dane formularza lub wysyłać dane do dozwolonych celów. CSP strony nie stanowi też niezawodnej bariery dla uprzywilejowanych rozszerzeń: Chrome oddziela izolowane content scripts od głównego kontekstu, w którym obowiązuje polityka strony.

1. Inwentaryzacja rzeczywistych zależności
Spis obejmuje skrypty, style, obrazy, czcionki, ramki i połączenia po interakcji. Istotne są rodzaje stron, języki, logowanie, zgoda i formularze. Menedżer tagów może dołączać dodatkowe cele, więc sam host kontenera nie tworzy pełnej listy zależności. Matomo Cloud wymaga rzeczywistych adresów instancji zamiast ogólnego dopuszczenia matomo.cloud.
’self’ oznacza ten sam origin, obejmujący schemat, host i port, a nie wszystkie subdomeny ani ten sam fizyczny serwer. default-src zapewnia wartość zastępczą dla określonych dyrektyw pobierania, ale nie dla base-uri, form-action i frame-ancestors. connect-src kontroluje połączenia przeglądarki, takie jak fetch, XHR, sendBeacon i WebSocket. CSP przeglądarki nie kontroluje dalszych żądań kontenera server-side GTM do platform reklamowych.
2. Początek od polityki bez blokowania
Każda dyrektywa to reguła dla określonego zasobu lub działania. Na przykład script-src kontroluje skrypty, form-action cele wysyłania formularzy, a frame-ancestors witryny, które mogą osadzać tę stronę. Poniższa polityka inwentaryzacyjna jest punktem wyjścia dla Report-Only w środowisku testowym. Skrypty i style inline są zapisane bezpośrednio w HTML zamiast w osobnych plikach. Te elementy oraz zasoby dostawców zewnętrznych nie zostały jeszcze dopuszczone. Typowa instalacja WordPressa wygeneruje zatem naruszenia. Bez dostosowania nie jest to gotowa konfiguracja produkcyjna.
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
frame-src 'none';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'self';
Nagłówek Content-Security-Policy-Report-Only zgłasza wykryte naruszenia, ale ta polityka nie blokuje zasobów. Jednoczesna polityka blokująca nadal działa. Dopiero po sprawdzeniu wymaganych zasobów i procesów uzgodniona polityka może zostać włączona jako Content-Security-Policy. 'unsafe-inline’ ponownie dopuszcza szeroki zakres skryptów inline i nie zastępuje celowego zezwalania na poszczególne skrypty.
3. Jednoznaczna odpowiedzialność za nagłówki
Nagłówek odpowiedzi HTTP przekazuje informacje lub instrukcje wraz z treścią strony. Nginx może dostarczać statyczną politykę w takim nagłówku. Poniższy fragment należy do odpowiedniego istniejącego kontekstu server lub location; TLS, proxy, PHP i pozostałe nagłówki pozostają częścią pełnej konfiguracji. Nagłówek trafia z odpowiedzią do przeglądarki i dopiero tam jest oceniany. Nie zatrzymuje to wykonania PHP ani nie wykrywa ataku przed uruchomieniem PHP.
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self';" always;
Przed planowanym przeładowaniem nginx -t sprawdza pełną konfigurację. Błąd powinien zatrzymać przeładowanie. Fragment przeszedł kontrolę składni w osobnej konfiguracji minimalnej; nie stanowi to weryfikacji istniejącej konfiguracji produkcyjnej.
sudo nginx -t && sudo systemctl reload nginx
Przy domyślnym dziedziczeniu add_header kontekst podrzędny z własnymi dyrektywami add_header przestaje dziedziczyć dyrektywy nadrzędne. always zmienia objęte statusy odpowiedzi, a nie dziedziczenie. Wspólny plik include pomaga zachować spójność. Nginx 1.29.3 wprowadził też add_header_inherit merge; opcja wymaga obsługiwanej wersji i kontroli niezamierzonych podwójnych nagłówków.
Bez dostępu do konfiguracji Nginx mała wtyczka WordPress przygotowana dla witryny może dodawać Report-Only do zwykłych odpowiedzi frontendu. To alternatywna ścieżka dostarczania, nie automatyczny mechanizm zapasowy Nginx. Przykład nie jest związany z WordPress 7.0.2 i nie obejmuje automatycznie panelu, logowania, REST ani plików cache wydawanych bezpośrednio.
<?php
/** Plugin Name: Example CSP Report-Only */
add_action('send_headers', static function () {
if (is_admin() || headers_sent()) {
return;
}
$policy = "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self';";
header('Content-Security-Policy-Report-Only: ' . $policy);
});
Hook send_headers pozwala dodawać nagłówki odpowiedzi. Wcześniejszy output oraz pamięć podręczna przed aplikacją wymagają uwzględnienia. Kilka blokujących nagłówków CSP obowiązuje równocześnie; zezwolenie w jednym nie uchyla ograniczenia drugiego. Rzeczywiste nagłówki wymagają sprawdzenia w CDN, reverse proxy, serwerze WWW i aplikacji.
4. Zezwalanie na skrypty przez nonce lub hash
Nonce to kryptograficznie losowa, nieprzewidywalna wartość dla każdej nowej odpowiedzi HTML. Nagłówek i jawnie zaufane elementy script otrzymują tę samą wartość. Nonce akcji WordPressa nie nadaje się do tego celu. Zbiorczy filtr HTML oznaczający każdy skrypt mógłby dopuścić również wstrzyknięty kod. Cache i generowanie HTML muszą być skoordynowane z nonce.
Poniższy samodzielny dokument PHP pokazuje tę zależność, ale nie jest pełną poprawką WordPressa. no-store zapobiega w przykładzie zamierzonemu ponownemu użyciu zapisanej odpowiedzi. Rzeczywista aplikacja wymaga celowego przekazania nonce do wszystkich potrzebnych zaufanych skryptów.
<?php
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: default-src 'none'; script-src 'nonce-$nonce'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'");
header('Cache-Control: no-store');
header('Content-Type: text/html; charset=utf-8');
?>
<!doctype html><html lang="en"><meta charset="utf-8">
<title>Nonce example</title><p id="result">Pending</p>
<script nonce="<?= htmlspecialchars($nonce, ENT_QUOTES, 'UTF-8') ?>">
document.getElementById('result').textContent = 'Allowed script ran';
</script></html>
Hash to obliczony odcisk treści skryptu. Hashe pasują do dokładnie znanej treści skryptu; zmiana treści lub białych znaków może zmienić hash. Nonce elementu script nie dopuszcza atrybutów zdarzeń inline, takich jak onclick. 'strict-dynamic’ może przekazywać zaufanie dynamicznie dołączanym skryptom i ogranicza znaczenie list hostów w obsługujących przeglądarkach. Takie przekazanie zaufania wymaga jawnej oceny. Dokumentacja GTM opisuje osadzenie obsługujące nonce oraz dodatkowe dyrektywy dla konkretnych funkcji tagów.
5. Oddzielna kontrola raportów i działania
Konsola przeglądarki to widok komunikatów w narzędziach deweloperskich. Bez adresu raportowania stanowi lokalne źródło diagnostyczne. Reporting-Endpoints i report-to mogą wskazać wcześniej skonfigurowany odbiornik HTTPS, a report-uri stanowi starszą ścieżkę zgodności. Nowoczesne raportowanie i report-uri używają różnych formatów JSON. Dostarczenie nie jest gwarantowane: obsługa przeglądarki, blokery i błędy sieci ograniczają kompletność. Adresy URL oraz ewentualne fragmenty skryptów w raportach wymagają ostrożnego przetwarzania i ograniczonej retencji.
Reporting-Endpoints: csp="https://reports.example.com/csp"
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp; report-uri https://reports.example.com/csp-legacy
Adresy są przykładami wymagającymi rzeczywistych odbiorników. Zgłoszone źródło nie powinno automatycznie trafiać na listę dozwolonych. Odbiór obejmuje nagłówki dokumentów, przekierowania, strony błędów, cache, formularze, multimedia, zmiany zgody i żądania śledzące. Sam HTTP 200 nie dowodzi poprawnego zebrania zdarzenia ani przetworzenia w systemie docelowym.
Pytania i odpowiedzi
Czy connect-src zatrzymuje każdy wyciek?
Nie. Dyrektywa kontroluje określone rodzaje połączeń, a nie wszystkie drogi wyjściowe lub dozwolone cele. Inne dyrektywy dotyczą na przykład obrazów i formularzy. Już dopuszczony kod, przejęte własne punkty końcowe i błędy logiki aplikacji pozostają osobnymi zagrożeniami.
Czy Report-Only działa przez element meta?
Nie. Report-Only wymaga nagłówka odpowiedzi HTTP. Nawet blokująca CSP w meta nie obsługuje wszystkich funkcji nagłówka, między innymi frame-ancestors. Podstawą spójnego wdrożenia jest rzeczywisty nagłówek odpowiedzi dokumentu.
Źródła
OWASP Top 10 · WordPress: functions.php i mechanizmy rozszerzeń
Zrozum przez eksperyment
Które skrypty dopuści ta CSP?
Zbuduj prostą regułę script-src. Przykładowa strona pokazuje, co byłoby dozwolone dla czterech fikcyjnych skryptów.
Model jednej dyrektywy script-src. Nie pobiera ani nie uruchamia fikcyjnych skryptów. Bez strict-dynamic, hashy, fallbacków i wielu polityk. Prawdziwy nonce musi być nieprzewidywalny i nowy dla każdej odpowiedzi; demo to tylko przykład. Dozwolone skrypty zewnętrzne nadal wymagają zaufania.
Ten moduł działa w przeglądarce i nie wysyła ani nie zapisuje wpisanych danych.
Zrozum przez eksperyment
Jedna minuta: znajdziesz błąd?
script-src 'self' 'nonce-demo' 'unsafe-inline': czy skrypt inline bez pasującego nonce może działać w tym modelu?
W tej regule CSP3 unsafe-inline jest pomijane z powodu źródła nonce. Skrypt inline potrzebuje pasującego nonce. Samo self nie dopuszcza skryptów inline.
Pojęcia do wypróbowania
Odkrywaj
Pojęcia do wypróbowania
Krótkie wyjaśnienie i mały eksperyment pomagają zrozumieć pojęcie.
Nonce
W CSP losowa wartość odnawiana dla każdej odpowiedzi może dopuścić konkretny skrypt. Nonce skryptu musi pasować do wartości w nagłówku.
Ćwiczenia obliczają wyniki w przeglądarce. Moduł nauki nie wysyła Twoich odpowiedzi.