Security headers: HSTS, CSP, X-Frame-Options en andere
· 7 min leestijd
De backend is een fort, maar de andere helft van je verdediging zit in de browser. HSTS, CSP en andere headers blokkeren XSS, clickjacking en MITM - gratis.
Waarom ze ertoe doen
Een applicatie kan op de backend perfect veilig zijn en toch kwetsbaar blijven voor aanvallen aan de browserkant: cross-site scripting (XSS), clickjacking, protocol downgrade, MIME confusion. Security headers verplaatsen de verdediging naar de browser zelf.
Strict-Transport-Security (HSTS)
Dwingt HTTPS af voor alle toekomstige bezoeken. Na het eerste bezoek via HTTPS onthoudt de browser het domein en herschrijft uit zichzelf elke http://-link naar https://, zelfs als de gebruiker op een verkeerde link klikt.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
max-age=63072000= geldig 2 jaarincludeSubDomains= de regel geldt ook voor alle subdomeinen; ze forceert HTTPS overal, dus een subdomein dat alleen via HTTP draait, bijv.staging.example.com, wordt onbereikbaar in de browserpreload= maakt opname mogelijk in de HSTS preload-lijst die in Chrome / Firefox / Safari is ingebouwd
Let op:
Een HSTS met preload uit de lijst verwijderen duurt 6+ maanden. Voeg preload niet toe voordat je hebt geverifieerd dat HTTPS stabiel werkt op alle subdomeinen.
Content-Security-Policy (CSP)
De krachtigste, maar ook moeilijkst in te stellen header. Hij whitelist van waar de browser scripts, stijlen, afbeeldingen en iframes mag laden. Zonder CSP kan een aanvaller die een <script>-tag kan injecteren willekeurige JS uitvoeren; met CSP alleen code uit goedgekeurde bronnen.
Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-...'; img-src 'self' data: https://cdn.example.com; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'
Gangbare directives:
default-src 'self'= sta standaard alleen resources van mijn eigen domein toescript-src= JS-bronnen (geef voor inline scripts een hash of nonce op)style-src= CSS-bronnenimg-src= afbeeldingenconnect-src= AJAX, WebSocket, EventSourceframe-ancestors 'none'= niemand mag de pagina in een iframe embedden (beter dan X-Frame-Options)report-uri /csp-report= de browser stuurt bij elke schending een JSON (jij vangt het op in de backend en logt het)- Let op:
report-uriis tegenwoordig deprecated - browsers diereport-toondersteunen, negeren het. De moderne manier is dereport-to-directive plus een aparteReporting-Endpoints-header; houdreport-urialleen als fallback voor Firefox, datreport-tonog niet ondersteunt.
X-Frame-Options
Ouder alternatief voor frame-ancestors. Voorkomt clickjacking, waarbij de aanvaller je pagina als iframe insluit en er onzichtbare knoppen overheen legt.
X-Frame-Options: DENY
Waarden: DENY (niemand), SAMEORIGIN (alleen mijn domein), ALLOW-FROM uri (deprecated).
X-Content-Type-Options
X-Content-Type-Options: nosniff
Schakelt MIME sniffing uit, zodat de browser het Content-Type respecteert dat de server retourneerde. Zonder dit kan een aanvaller een bestand met een verkeerd type uploaden (bijv. een afbeelding die eigenlijk HTML met script is) en kan de browser het als webpagina uitvoeren.
Referrer-Policy
Referrer-Policy: strict-origin-when-cross-origin
Bepaalt hoeveel informatie over de vorige pagina de browser bij een klik in de Referer-header verzendt. De standaardwaarde in moderne browsers is al strict-origin-when-cross-origin, maar de header expliciet instellen garandeert consistent gedrag.
Permissions-Policy
Schakelt API's uit die je niet nodig hebt, zoals camera, microfoon, GPS en geolocatie. Een aanvaller die XSS misbruikt, kan deze API's niet aanvragen.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Praktische nginx-configuratie
server {
listen 443 ssl http2;
server_name example.com;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
# we hebben preload hier bewust weggelaten - voeg het pas toe als je 100% zeker bent (zie de preload-valkuil hierboven)
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# CSP is uitgebreid - definieer hem volgens je applicatie
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; ..." always;
}
De sleutel is always - zonder dat laat nginx de headers weg bij error responses (4xx, 5xx).
Audit van de huidige staat
Het eenvoudigst via een online tool. Onze header check toont je welke headers ontbreken en welke fout zijn. Voor een grondige audit geeft securityheaders.com bovendien een A-F score.
Conclusie
Security headers behoren tot de beste investeringen in beveiliging qua verhouding inspanning tot opbrengst: typisch een half uur setup in een reverse proxy tegen een hele categorie aanvallen aan de browserkant. Een audit eens per halfjaar als onderdeel van regulier onderhoud is een redelijk minimum.
Gratis security headers audit
Zonder registratie, resultaat in drie seconden.
Probeer ePulz.io gratis - 7 dagen zonder creditcard.
Account aanmaken