feat: implement security headers and rate limiting; update environment variables and documentation

This commit is contained in:
Nils-Johan Gynther
2026-04-21 08:06:21 +02:00
parent c1d51c771e
commit 7748ad311f
13 changed files with 133 additions and 23 deletions
+57
View File
@@ -214,6 +214,63 @@ recept.gynther.se {
---
## Säkerhetshuvuden
Säkerhetshuvuden är implementerade i tre lager för djupförsvar:
### Lager 1: Caddy (globalt för alla tjänster)
Sätts i `(common)`-blocket och gäller alla domäner som importerar det.
| Header | Värde | Syfte |
|--------|-------|-------|
| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains; preload` | Tvingar HTTPS, skyddar mot protocol downgrade |
| `X-Content-Type-Options` | `nosniff` | Förhindrar MIME-sniffing |
| `X-Frame-Options` | `DENY` | Skyddar mot clickjacking |
| `X-XSS-Protection` | `1; mode=block` | Legacy XSS-skydd (äldre webbläsare) |
| `Referrer-Policy` | `strict-origin-when-cross-origin` | Begränsar referrer-läckage |
| `Permissions-Policy` | `geolocation=(), microphone=(), camera=(), payment=()` | Inaktiverar känsliga webbläsar-API:er |
| `Cross-Origin-Opener-Policy` | `same-origin` | Isolerar browsing context |
| `Cross-Origin-Resource-Policy` | `same-origin` | Förhindrar cross-origin läsning av resurser |
| `Cross-Origin-Embedder-Policy` | `require-corp` | Kräver explicit cross-origin permission |
### Lager 2: NestJS Helmet (backup)
Helmet konfigurerat i `backend/src/main.ts` som säkerhetsbackup ifall Caddy kringgås eller misslyckas. CSP är inaktiverat i Helmet (`contentSecurityPolicy: false`) eftersom det hanteras av Next.js.
```
Aktiveras vid: docker compose up --build backend
```
### Lager 3: Next.js Content Security Policy
CSP sätts i `frontend/next.config.js` via `headers()`-funktionen och gäller alla routes (`/:path*`).
| Direktiv | Tillåtna källor | Motivering |
|----------|----------------|------------|
| `default-src` | `'self'` | Restriktiv default |
| `script-src` | `'self' 'unsafe-eval' 'unsafe-inline'` | Krävs av Next.js runtime |
| `style-src` | `'self' 'unsafe-inline' fonts.googleapis.com` | Inline-stilar + Google Fonts |
| `img-src` | `'self' data: https:` | Tillåter externa bilder via HTTPS |
| `font-src` | `'self' fonts.gstatic.com` | Google Fonts-filer |
| `connect-src` | `'self' api.mistral.ai` | API-anrop inkl. Mistral AI |
| `frame-src` | `'none'` | Inga inbäddade frames tillåtna |
| `object-src` | `'none'` | Inga plugins (Flash, etc.) |
| `base-uri` | `'self'` | Skyddar mot base-tag-injektion |
| `form-action` | `'self'` | Formulär får bara posta till samma origin |
> **Notering:** `'unsafe-eval'` och `'unsafe-inline'` i `script-src` är nödvändiga för Next.js 16 med App Router. Undvik att ta bort dessa utan noggrann testning.
### Felsökning av CSP-brott
Om en funktion slutar fungera efter CSP-aktivering:
1. Öppna webbläsarens devtools → Console för att se CSP-felmeddelanden
2. Kontrollera vilken domän/resurs som blockeras
3. Lägg till domänen i rätt direktiv i `frontend/next.config.js`
4. Vanliga undantag: WebSockets kräver `wss:` i `connect-src`, Service Workers kräver `worker-src 'self'`
---
## Arkitekturprincip: API routes framför Server Actions
> **Regel: Använd Next.js API routes (`/app/api/...`) för all mutation från klientkomponenter. Använd INTE Server Actions för detta.**