Brak Content-Security-Policy
Co to jest
Twoja witryna nie wysyła nagłówka Content-Security-Policy. Ten nagłówek mówi przeglądarce, z jakich źródeł wolno jej ładować skrypty, obrazy, fonty i ramki iframe.
Bez niego przeglądarka uruchomi dowolny kod, który pojawi się w HTML — bez względu na to, czy umieścił go tam Twój zespół, czy atakujący przez XSS.
Dlaczego to problem
CSP to jedyna skuteczna obrona przed cross-site scripting (XSS). Gdy atakujący znajdzie gdzieś w aplikacji lukę, przez którą wepchnie <script>, bez CSP przeglądarka z radością ten skrypt uruchomi. Z CSP przeglądarka powie „tego źródła nie ma na liście dozwolonych” i skrypt nie zadziała.
Druga płaszczyzna — ochrona przed wstrzykniętymi drainerami i spamem SEO. Jeśli masz poprawnie skonfigurowany script-src, atakujący przez zhakowaną wtyczkę nie może łatwo dodać zewnętrznego skryptu.
Jak to naprawić
CSP to polityka, która może popsuć stronę. Wdróż ją najpierw w trybie report-only.
Faza 1 — report-only
Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data: https:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; report-uri /csp-reportTa polityka niczego nie blokuje — przy każdym naruszeniu wysyła tylko JSON na /csp-report. Przez kilka dni obserwuj logi, aby wiedzieć, jakich skryptów/obrazów/fontów strona rzeczywiście używa.
Faza 2 — dostroić listę dozwolonych
Na podstawie raportów zbuduj konkretną listę dozwolonych. Przykład dla WordPressa z Google Fonts i analityką Umami:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://i.xhs.cz;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https://*.gravatar.com;
connect-src 'self' https://i.xhs.cz;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';Faza 3 — przełączyć na enforce
Gdy tryb report-only nie zgłasza już żadnego prawidłowego naruszenia, zmień nagłówek z Content-Security-Policy-Report-Only na Content-Security-Policy.
Uwaga na 'unsafe-inline'
Skrypty inline (<script>...</script>) oraz obsługa zdarzeń inline (onclick="...") wymagają 'unsafe-inline'. Wtedy jednak CSP traci większość swojej wartości przeciw XSS. Lepiej użyć nonce lub hash i stopniowo usuwać skrypty inline. WordPress ma dla nonce wbudowane wsparcie.
Konkretna implementacja
- nginx:
add_header Content-Security-Policy "..." always; - Apache:
Header always set Content-Security-Policy "..." - Wtyczka WordPress: HTTP Headers