Du har byggt din sajt, den ser snygg ut och allt fungerar. Men har du tänkt på vad som händer om någon lyckas injicera ett skript på din sida? Content Security Policy, eller CSP, är webbens brandvägg mot just sådant — och det är enklare att komma igång med än du tror.
Vad är Content Security Policy?
Content Security Policy är en HTTP-header som talar om för webbläsaren exakt vilka resurser din sida får ladda in. Det handlar om skript, stilmallar, bilder, fonter, iframes och mycket mer. Genom att sätta en CSP-policy bestämmer du vilka domäner som är tillåtna — allt annat blockeras.
Grundidén är enkel: istället för att försöka hitta och filtrera bort farlig kod (vilket alltid har luckor) säger du helt enkelt åt webbläsaren att bara köra kod från godkända källor.
Varför behöver du CSP?
Den vanligaste attacken CSP skyddar mot är Cross-Site Scripting (XSS). XSS innebär att en angripare lyckas injicera JavaScript på din sida — via ett formulärfält, en URL-parameter eller en komprometterad tredjepartstjänst. Utan CSP kör webbläsaren glatt allt JavaScript som dyker upp i DOM:en, oavsett varifrån det kommer.
Med en korrekt CSP-header ser webbläsaren att skriptet inte kommer från en tillåten källa och vägrar köra det. Attacken stoppas innan den ens hinner starta.
CSP hjälper även mot:
- Clickjacking — genom att kontrollera vilka sidor som får bädda in din sajt via iframes
- Data exfiltration — genom att begränsa vilka domäner din sida kan skicka data till
- Mixed content — genom att tvinga HTTPS för alla resurser
Hur ser en CSP-header ut?
En CSP-policy skickas som en HTTP-header och består av direktiv som styr olika resurstyper. Här är ett enkelt exempel:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src *;
Det här säger:
| Direktiv | Vad det gör |
|---|---|
default-src 'self' | I normalfallet, ladda bara resurser från den egna domänen |
script-src 'self' https://cdn.example.com | JavaScript får bara laddas från den egna domänen och CDN:et |
style-src 'self' 'unsafe-inline' | CSS från egna domänen, plus inline-stilar (behövs ofta i verkligheten) |
img-src * | Bilder får laddas från vilken domän som helst |
De viktigaste direktiven
Det finns ett gäng direktiv att välja mellan. Här är de du bör känna till:
| Direktiv | Kontrollerar | Vanlig inställning |
|---|---|---|
default-src | Fallback för alla resurstyper | 'self' |
script-src | JavaScript | 'self' |
style-src | CSS | 'self' 'unsafe-inline' |
img-src | Bilder | 'self' data: |
font-src | Fonter | 'self' https://fonts.gstatic.com |
connect-src | API-anrop, WebSockets | 'self' |
frame-src | Iframes | 'none' |
object-src | Plugins (Flash, Java) | 'none' |
base-uri | Vad <base>-taggen får peka på | 'self' |
form-action | Vart formulär får skickas | 'self' |
Report-Only: testa utan att något går sönder
Det största hindret för att införa CSP är rädslan att något slutar fungera. Och visst, sätter du en för strikt policy kan skript och stilmallar blockeras. Men det finns en lösning: Content-Security-Policy-Report-Only.
Istället för att blockera rapporterar webbläsaren bara överträdelser. Du kan skicka rapporterna till en endpoint och analysera dem i lugn och ro:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report;
Kör Report-Only i ett par veckor, kolla loggarna, justera policyn och aktivera den skarpt när du vet att allt fungerar. Det är det säkra sättet att rulla ut CSP utan att riskera driftstörningar.
Nonce och hash — den moderna vägen
Att tillåta 'unsafe-inline' för skript är som att sätta ett lås på dörren men lämna fönstret öppet. Det öppnar för XSS igen. Den moderna lösningen heter nonce eller hash.
En nonce (number used once) är ett slumpmässigt värde som genereras vid varje sidladdning. Du lägger det i headern och på dina script-taggar:
Content-Security-Policy: script-src 'nonce-abc123'
<script nonce="abc123">
// Det här skriptet körs
</script>
<script>
// Det här blockeras — ingen nonce
</script>
Med nonce kan du ha inline-skript utan att öppna upp för XSS. Varje sidladdning genererar en ny nonce, så en angripare kan inte gissa sig till rätt värde.
CSP i praktiken — Apache och Nginx
Att sätta headern är trivialt i de flesta webbservrar.
Apache (i .htaccess eller virtualhost):
Header set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; frame-ancestors 'none';"
Nginx:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; frame-ancestors 'none';" always;
PHP (i din applikation):
header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';");
Sitewide vs. global konfiguration — var ska CSP-headern ligga?
Det här är en avgörande detalj som ofta förbises: var du sätter din CSP-header spelar stor roll, särskilt om du kör flera sajter på samma server.
Det finns tre nivåer att välja mellan:
| Nivå | Fil / Plats | Påverkar |
|---|---|---|
| Global | apache2.conf, httpd.conf eller Nginx nginx.conf / http {}-blocket | Alla sajter på hela servern |
| Sitewide (vhost) | Apache: sites-available/example.confNginx: conf.d/example.conf eller sites-available/ | Bara den specifika sajten/domänen |
| Sitewide (.htaccess) | .htaccess i sajtens rot (bara Apache) | Bara den katalogen och underkataloger |
Global konfiguration
Lägger du CSP i den globala Apache- eller Nginx-konfigurationen gäller den för samtliga sajter på servern. Det kan vara rätt om du kör en server med en enda sajt, men det blir snabbt problematiskt om du har flera domäner med olika behov. En sajt kanske kör Google Analytics och en annan använder en extern chatt-widget — de behöver helt olika CSP-regler.
# Apache — global (påverkar ALLA sajter)
# /etc/apache2/apache2.conf
Header set Content-Security-Policy "default-src 'self';"
# Nginx — global (påverkar ALLA sajter)
# /etc/nginx/nginx.conf → http { }
add_header Content-Security-Policy "default-src 'self';" always;
Virtualhost-konfiguration (rekommenderat)
Den bästa platsen i de flesta fall är i virtualhost-filen för den specifika sajten. Då får varje domän sin egen policy och du riskerar inte att en ändring för en sajt knäcker en annan.
# Apache — per sajt
# /etc/apache2/sites-available/example.se.conf
<VirtualHost *:443>
ServerName example.se
Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://analytics.example.com;"
</VirtualHost>
# Nginx — per sajt
# /etc/nginx/sites-available/example.se
server {
server_name example.se;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://analytics.example.com;" always;
}
.htaccess — flexibelt men långsammare
.htaccess fungerar bara i Apache och läses vid varje request, vilket gör det marginellt långsammare. Fördelen är att du kan ändra CSP utan att ladda om webbservern, och att det ligger i sajtens katalog snarare än i serverkonfigurationen.
# /var/www/example.se/.htaccess
Header set Content-Security-Policy "default-src 'self'; script-src 'self';"
Varför det spelar roll — särskilt vid AI-assisterad utveckling
Om du använder AI-verktyg (som GitHub Copilot, Claude eller ChatGPT) för att konfigurera din server, är det kritiskt att du anger exakt var ändringen ska göras. Ber du en AI att "lägga till CSP-headers" utan att specificera var, kan den redigera den globala konfigurationen — och plötsligt har alla sajter på servern en ny policy som kanske knäcker tredjepartsintegrationer.
En bra tumregel:
- Säg alltid vilken sajt det gäller — "Lägg till CSP i vhost-filen för example.se", inte bara "Lägg till CSP"
- Peka på rätt fil — ange filsökväg, t.ex.
/etc/apache2/sites-available/example.se.conf - Undvik globala ändringar om du inte medvetet vill ha samma policy på alla sajter
- Granska alltid diff:en — kontrollera att ändringen hamnade på rätt ställe innan du tillämpar den
Det här gäller inte bara CSP utan alla HTTP-headers och serverkonfigurationer. Men CSP är extra känsligt eftersom en felaktig policy direkt kan slå ut funktionalitet på sajten.
Vanliga misstag att undvika
- Att använda
unsafe-inlineochunsafe-evali script-src — det gör hela CSP-skyddet mot XSS verkningslöst - Att tillåta för breda domäner —
script-src https:tillåter skript från vilken HTTPS-domän som helst, inklusive angriparens - Att glömma
default-src— utan fallback kan resurser laddas obegränsat - Att inte testa med Report-Only först — du riskerar att knäcka tredjepartstjänster som analytics, chatt-widgets och betalningslösningar
- Att sätta CSP och sedan glömma bort det — din sajt förändras, och policyn måste uppdateras i takt med nya integrationer
En bra startpolicy
Om du aldrig har använt CSP förut, börja med det här:
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self';
frame-ancestors 'none';
object-src 'none';
base-uri 'self';
form-action 'self';
report-uri /csp-report;
Kör det i Report-Only-läge, analysera rapporterna och lägg till de domäner du faktiskt behöver. Sedan byter du till den skarpa headern Content-Security-Policy.
Sammanfattning
CSP är ett av de kraftfullaste verktygen du har för att skydda din webbplats. Det kräver lite planering — du behöver veta vilka resurser din sajt laddar — men vinsten är enorm. En väl konfigurerad CSP-policy:
- Stoppar XSS-attacker innan de når användarens webbläsare
- Begränsar skadan om en tredjepartstjänst komprometteras
- Ger dig kontroll över exakt vilka resurser din sajt får använda
- Höjer din säkerhetsprofil i verktyg som Mozilla Observatory och SecurityHeaders.com
Behöver du hjälp med webbsäkerhet?
Vi hjälper dig att konfigurera CSP och andra säkerhetsheaders — rätt från början, utan att knäcka din sajt. Oavsett om du kör Apache, Nginx eller PHP kan vi sätta upp en policy som skyddar dina besökare och höjer din säkerhetsprofil.
Kontakta oss