security(cards): CSP report-only + service-key rotation playbook
Some checks are pending
CI / validate (push) Waiting to run
Some checks are pending
CI / validate (push) Waiting to run
Folge-Hardening zu e1ddbf3, Cluster A2+A3 aus FEATURE_IDEAS.
* hooks.server.ts: restriktive CSP im Report-Only-Modus
(default-src 'self', script-src 'self', connect-src whitelist
auf cardecky-api/auth.mana.how/share/mcp). CARDS_CSP_ENFORCE=true
flippt auf den scharfen Header.
* docs/playbooks/SERVICE_KEY_ROTATION.md: 5-Schritt-Rotation für
CARDS_DSGVO_SERVICE_KEY bis Phase F-1 (mana-auth-managed Keys).
Forensik der Bypass-Periode 2026-05-08 → 2026-05-12 ist abgeschlossen:
nur 2 user_ids in der Cards-DB, beide legitim (tills95@gmail.com +
Smoke-Test-Sentinel c1a5, letztere via DSGVO-Endpoint aufgeräumt).
Kein ausgenutzter Bypass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e1ddbf34b3
commit
5a29dd9a8c
3 changed files with 164 additions and 14 deletions
|
|
@ -3,21 +3,45 @@ import type { Handle } from '@sveltejs/kit';
|
|||
/**
|
||||
* Server-Hook für Security-Headers auf jeder ausgelieferten Response.
|
||||
*
|
||||
* Bewusst minimalistisch: nur die well-known Header, die ohne
|
||||
* Browser-Verhaltens-Test sicher sind. **CSP fehlt absichtlich** —
|
||||
* eine richtige Content-Security-Policy braucht App-Inventur
|
||||
* (inline-styles, externe Theme-Assets, Markdown-Renderer) und
|
||||
* sollte separat mit Live-Test eingeführt werden.
|
||||
*
|
||||
* Cloudflare-Tunnel-Transform-Rules dürfen das gerne nochmal
|
||||
* obendrauf setzen — die hier sind die Defense-Tiefe-Schicht für
|
||||
* den Fall, dass die Cloudflare-Schiene aus dem Pfad fällt.
|
||||
*
|
||||
* CSP läuft initial im **Report-Only-Mode**: Browser melden
|
||||
* Violations in die DevTools-Console (`console.warn`), die App
|
||||
* bleibt funktional. Nach 1-2 Tagen Live-Beobachtung muss die
|
||||
* Policy mit echten Reports getunet und auf Enforce geflippt
|
||||
* werden — `CARDS_CSP_ENFORCE=true` aktiviert den scharfen Header.
|
||||
*/
|
||||
const CSP_POLICY = [
|
||||
"default-src 'self'",
|
||||
// 'unsafe-inline' für Svelte-5-Style-Attribute + Theme-CSS-Variables;
|
||||
// langfristig durch nonce-/hash-basierten Style ersetzen.
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"script-src 'self'",
|
||||
"img-src 'self' data: blob:",
|
||||
"font-src 'self' data:",
|
||||
// XHR/fetch-Ziele: eigene API + Auth-Portal + LLM/Share/MCP-Plattform.
|
||||
"connect-src 'self' https://cardecky-api.mana.how https://auth.mana.how https://share.mana.how https://mcp.mana.how",
|
||||
"media-src 'self' blob:",
|
||||
"frame-ancestors 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self' https://auth.mana.how",
|
||||
"object-src 'none'",
|
||||
].join('; ');
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
const response = await resolve(event);
|
||||
response.headers.set('X-Frame-Options', 'DENY');
|
||||
response.headers.set('X-Content-Type-Options', 'nosniff');
|
||||
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
|
||||
const cspHeader =
|
||||
process.env.CARDS_CSP_ENFORCE === 'true'
|
||||
? 'Content-Security-Policy'
|
||||
: 'Content-Security-Policy-Report-Only';
|
||||
response.headers.set(cspHeader, CSP_POLICY);
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
response.headers.set('Strict-Transport-Security', 'max-age=15552000; includeSubDomains');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue