Skip to content

Sikkerhed & CSP

Scope

frame-ancestors, iframe-sandbox og CORS-for-loader.js gælder kun embeddable widgets. Origin-isolation og immutable-caching gælder alle widgets på *.widget.greenbow.dk, inkl. standalone widgets som Checkout Return.

Origin-isolation

Hver widget har sit eget subdomæne ({slug}.widget.greenbow.dk). Det betyder:

  • Cookies, localStorage, sessionStorage og IndexedDB er isoleret pr. widget. En kompromitteret widget kan ikke læse data fra en anden Greenbow widget.
  • CSP og Permissions-Policy fungerer pr. origin — vi kan stramme policies pr. widget uden at påvirke andre.
  • X-Frame-Options er deprecated og bruges ikke. Vi bruger Content-Security-Policy: frame-ancestors i stedet.

Frame-ancestors

Hver widget tillader kun at blive iframet fra greenbow.dk og en hvidliste af godkendte partner-domæner:

nginx
add_header Content-Security-Policy
  "frame-ancestors 'self' https://greenbow.dk https://*.greenbow.dk https://partner1.dk"
  always;

Listen vedligeholdes i nginx.conf.example (rod af repoet) og synces til DO App Platform pr. deploy.

Iframe sandbox

Loaderen sætter sandbox="allow-scripts allow-same-origin allow-popups allow-forms" på iframen. Det giver widgeten lov til:

  • At køre scripts (nødvendigt — det er en SPA)
  • At have sit eget origin (nødvendigt for cookies, fetch, storage)
  • At åbne popups (f.eks. links til greenbow.dk)
  • At submitte forms

Det blokerer eksplicit fra:

  • allow-top-navigation — widgeten kan ikke redirecte host-siden
  • allow-modals — widgeten kan ikke vise alert/confirm/prompt på host-siden
  • allow-pointer-lock, allow-orientation-lock osv.

CORS for loader.js

Loader-scriptet skal kunne loades fra alle partner-origins:

nginx
location = /loader.js {
  add_header Access-Control-Allow-Origin "*" always;
  add_header Cache-Control "public, max-age=300" always;
}

Den korte cache (max-age=300) er bevidst — loaderen er den ene fil vi kan rette uden at deploye en ny widget-version. Hvis vi finder en bug i loader-logikken, ruller fixet ud globalt på 5 minutter.

Den faktiske app er immutable cached

Versionerede paths som /v1/, /v2/ cached i et år:

nginx
location ~ ^/v\d+/ {
  add_header Cache-Control "public, max-age=31536000, immutable" always;
}

Bygget af Greenbow