Content-Security-Policy (CSP) to nagłówek HTTP, który chroni stronę przed atakami XSS i innymi zagrożeniami. Jednak zbyt restrykcyjny CSP może zablokować usługi Google jak GTM, Analytics czy Maps. Ten przewodnik pokazuje jak skonfigurować CSP, aby działały wszystkie potrzebne usługi.
Czym jest Content-Security-Policy?
CSP definiuje dozwolone źródła dla różnych typów zasobów (skrypty, style, obrazy, fonty). Przeglądarka blokuje wszystko, co nie jest jawnie dozwolone.
Podstawowa składnia
Content-Security-Policy: directive source1 source2; directive2 source3;
Główne dyrektywy
| Dyrektywa | Kontroluje |
|---|---|
default-src | Domyślne źródło dla wszystkich typów |
script-src | Skrypty JavaScript |
style-src | Style CSS |
img-src | Obrazy |
font-src | Fonty |
connect-src | XHR, fetch, WebSocket |
frame-src | Iframes |
object-src | Pluginy (Flash, Java) |
Wartości źródeł
| Wartość | Znaczenie |
|---|---|
'self' | Ta sama domena |
'none' | Blokuj wszystko |
'unsafe-inline' | Dozwól inline (niebezpieczne!) |
'unsafe-eval' | Dozwól eval() (niebezpieczne!) |
'nonce-xyz' | Tylko skrypty z tym nonce |
'strict-dynamic' | Zaufaj skryptom ładowanym przez zaufane |
https: | Wszystkie źródła HTTPS |
*.example.com | Subdomena |
CSP dla Google Tag Manager
GTM wymaga wielu domen i funkcjonalności. Konfiguracja zależy od trybu (client-side vs server-side).
GTM Client-Side - minimalna konfiguracja
Content-Security-Policy:
script-src 'self' 'unsafe-inline' 'unsafe-eval'
https://www.googletagmanager.com
https://tagmanager.google.com;
img-src 'self' data:
https://www.googletagmanager.com
https://www.google-analytics.com
https://ssl.gstatic.com
https://www.gstatic.com;
connect-src 'self'
https://www.google-analytics.com
https://analytics.google.com
https://stats.g.doubleclick.net
https://region1.google-analytics.com;
frame-src
https://www.googletagmanager.com;
Problem: GTM wymaga unsafe-inline i unsafe-eval
GTM dynamicznie wstrzykuje skrypty, co wymaga 'unsafe-inline'. Custom HTML tags mogą używać eval(). To znacząco osłabia CSP.
Rozwiązanie 1: Nonce dla GTM
<!-- Generuj nonce po stronie serwera -->
<script nonce="abc123">
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;
j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;
j.nonce='abc123';
f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXX');
</script>
Content-Security-Policy:
script-src 'self' 'nonce-abc123' 'strict-dynamic'
https://www.googletagmanager.com;
Rozwiązanie 2: Server-Side GTM
Server-side GTM znacząco upraszcza CSP:
Content-Security-Policy:
script-src 'self' 'nonce-abc123'
https://gtm.yourdomain.com;
connect-src 'self'
https://gtm.yourdomain.com;
CSP dla Google Analytics 4
GA4 z gtag.js
Content-Security-Policy:
script-src 'self'
https://www.googletagmanager.com;
img-src 'self'
https://www.google-analytics.com
https://www.googletagmanager.com;
connect-src 'self'
https://www.google-analytics.com
https://analytics.google.com
https://region1.google-analytics.com
https://region2.google-analytics.com
https://region3.google-analytics.com;
GA4 z GTM
Jeśli ładujesz GA4 przez GTM, potrzebujesz kombinacji powyższych.
CSP dla Google Maps
Maps JavaScript API
Content-Security-Policy:
script-src 'self' 'unsafe-inline'
https://maps.googleapis.com
https://maps.gstatic.com;
img-src 'self' data: blob:
https://maps.googleapis.com
https://maps.gstatic.com
https://*.ggpht.com
https://*.google.com
https://*.googleapis.com;
style-src 'self' 'unsafe-inline'
https://fonts.googleapis.com;
font-src 'self'
https://fonts.gstatic.com;
connect-src 'self'
https://maps.googleapis.com
https://places.googleapis.com;
frame-src
https://www.google.com;
worker-src blob:;
Maps Embed API (iframe)
Content-Security-Policy:
frame-src
https://www.google.com
https://maps.google.com;
CSP dla Google Fonts
Content-Security-Policy:
style-src 'self'
https://fonts.googleapis.com;
font-src 'self'
https://fonts.gstatic.com;
Alternatywa: Self-hosting fontów
Hostując fonty lokalnie, eliminujesz zewnętrzne zależności:
Content-Security-Policy:
font-src 'self';
style-src 'self';
CSP dla Google reCAPTCHA
reCAPTCHA v2/v3
Content-Security-Policy:
script-src 'self'
https://www.google.com/recaptcha/
https://www.gstatic.com/recaptcha/;
frame-src
https://www.google.com/recaptcha/
https://recaptcha.google.com;
style-src 'self' 'unsafe-inline';
CSP dla YouTube embeds
Content-Security-Policy:
frame-src
https://www.youtube.com
https://www.youtube-nocookie.com;
img-src 'self'
https://i.ytimg.com
https://img.youtube.com;
CSP dla Google Ads
Google Ads Conversion Tracking
Content-Security-Policy:
script-src 'self' 'unsafe-inline'
https://www.googleadservices.com
https://www.googletagmanager.com
https://googleads.g.doubleclick.net;
img-src 'self'
https://www.google.com
https://www.google.pl
https://googleads.g.doubleclick.net
https://www.googleadservices.com;
connect-src 'self'
https://www.google.com
https://pagead2.googlesyndication.com;
frame-src
https://bid.g.doubleclick.net
https://tpc.googlesyndication.com;
Google AdSense
Content-Security-Policy:
script-src 'self' 'unsafe-inline' 'unsafe-eval'
https://pagead2.googlesyndication.com
https://adservice.google.com
https://www.googletagservices.com
https://partner.googleadservices.com;
img-src 'self' data:
https://pagead2.googlesyndication.com
https://tpc.googlesyndication.com
https://*.google.com;
frame-src
https://googleads.g.doubleclick.net
https://tpc.googlesyndication.com
https://www.google.com;
style-src 'self' 'unsafe-inline';
Kompletna konfiguracja dla typowej strony
Strona z GTM, GA4, Maps i Fonts
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval'
https://www.googletagmanager.com
https://tagmanager.google.com
https://www.google-analytics.com
https://maps.googleapis.com
https://maps.gstatic.com;
style-src 'self' 'unsafe-inline'
https://fonts.googleapis.com
https://tagmanager.google.com;
img-src 'self' data: blob:
https://www.googletagmanager.com
https://www.google-analytics.com
https://ssl.gstatic.com
https://www.gstatic.com
https://maps.googleapis.com
https://maps.gstatic.com
https://*.ggpht.com
https://*.google.com;
font-src 'self'
https://fonts.gstatic.com;
connect-src 'self'
https://www.google-analytics.com
https://analytics.google.com
https://region1.google-analytics.com
https://stats.g.doubleclick.net
https://maps.googleapis.com;
frame-src
https://www.googletagmanager.com
https://www.google.com
https://www.youtube.com;
object-src 'none';
base-uri 'self';
form-action 'self';
Implementacja CSP
Apache (.htaccess)
Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..."
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..." always;
Node.js (Express)
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://www.googletagmanager.com"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
imgSrc: ["'self'", "data:", "https://www.google-analytics.com"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
connectSrc: ["'self'", "https://www.google-analytics.com"],
frameSrc: ["https://www.googletagmanager.com"]
}
}));
Netlify (netlify.toml)
[[headers]]
for = "/*"
[headers.values]
Content-Security-Policy = "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..."
Vercel (vercel.json)
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..."
}
]
}
]
}
Testowanie CSP
1. Report-Only Mode
Zacznij od trybu raportowania, który nie blokuje, tylko loguje:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report;
2. Przeglądarka DevTools
Console pokaże błędy CSP:
Refused to load the script 'https://example.com/script.js' because it violates the Content Security Policy directive: "script-src 'self'".
3. CSP Evaluator
https://csp-evaluator.withgoogle.com/ - narzędzie Google do analizy CSP.
4. Observatory by Mozilla
https://observatory.mozilla.org/ - kompleksowy audyt bezpieczeństwa.
Raportowanie naruszeń CSP
Endpoint raportowania
Content-Security-Policy: default-src 'self'; report-uri /csp-report; report-to csp-endpoint;
Odbieranie raportów (Node.js)
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
console.log('CSP Violation:', req.body['csp-report']);
res.status(204).end();
});
Best Practices
1. Zacznij restrykcyjnie
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self';
Dodawaj źródła dopiero gdy są potrzebne.
2. Unikaj unsafe-inline i unsafe-eval
Jeśli to możliwe, używaj nonce lub hash zamiast 'unsafe-inline':
<script nonce="random123">
// Twój kod
</script>
Content-Security-Policy: script-src 'nonce-random123';
3. Używaj strict-dynamic
'strict-dynamic' pozwala zaufanym skryptom ładować kolejne:
Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic';
4. Rozważ Server-Side GTM
Server-side tagging drastycznie upraszcza CSP i zwiększa bezpieczeństwo.
5. Regularnie aktualizuj
Google może zmieniać domeny. Monitoruj raporty CSP i aktualizuj politykę.
Podsumowanie
CSP dla Google Services wymaga balansowania między bezpieczeństwem a funkcjonalnością:
- GTM wymaga kompromisów -
'unsafe-inline'jest często niezbędne - Server-side GTM znacząco upraszcza CSP
- Nonce + strict-dynamic to najlepsza praktyka
- Testuj w Report-Only przed wdrożeniem
- Monitoruj naruszenia przez raportowanie
Dobrze skonfigurowany CSP chroni użytkowników przed XSS bez blokowania funkcjonalności strony.
Źródła
-
MDN - Content-Security-Policy https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
-
Google - CSP for Google Tag Manager https://developers.google.com/tag-platform/tag-manager/csp
-
Google - CSP Evaluator https://csp-evaluator.withgoogle.com/
-
web.dev - Content Security Policy https://web.dev/articles/csp
-
Google Maps Platform - CSP https://developers.google.com/maps/documentation/javascript/content-security-policy



