import { getBaseUrl } from "@/lib/url"; /** * Same-Origin-Pruefung fuer zustandsaendernde app/api/.../route.ts-Handler. * * Next.js' eingebauter CSRF-Schutz (Origin-Header gegen Host geprueft) gilt * nur fuer Server Actions ("use server") — eigene Route-Handler wie * /api/checkout sind normale POST-Endpunkte ohne diesen automatischen Schutz * und muessen ihn sich selbst holen. Ohne diese Pruefung liesse sich ein * state-aendernder POST z.B. per
als "einfacher" * (nicht preflight-pflichtiger) Cross-Origin-Request faelschen (bekannte * JSON-CSRF-Umgehung) — bei /api/checkout koennte das, sofern der Besuch von * einer eingeloggten Kundschaft ausgeht, Bestellungen unter deren Konto * anlegen und Lagerbestand reservieren, ohne dass die Kundschaft das wollte. * * Akzeptiert den Origin, wenn er zu request.url ODER zu getBaseUrl() * (NEXT_PUBLIC_SITE_URL) passt — nicht nur zu request.url allein. Hinter * einem selbst betriebenen Reverse-Proxy (Caddy/nginx, siehe * docker-compose.yml bzw. k8s/) ist der vom Next.js-Prozess intern * rekonstruierte Host aus request.url nicht in jeder Proxy-Konstellation * zuverlaessig derselbe wie der echte, oeffentliche Host, den der Browser im * Origin-Header sendet (vgl. denselben Grundsatz bereits in lib/url.ts) — * das fuehrte live faelschlich zu "Ungueltige Anfrage." bei jedem * Admin-Upload, obwohl der Request tatsaechlich vom eigenen Frontend kam. * Die zusaetzliche ODER-Bedingung statt eines reinen Ersatzes ist bewusst * risikoarm: sie akzeptiert nur MEHR legitime Faelle als vorher, verengt * nichts — verhindert also, dass ein falsch/veraltet gesetztes * NEXT_PUBLIC_SITE_URL ploetzlich bislang funktionierende Endpunkte wie * /api/checkout fuer echte Kundschaft blockiert. */ export function isTrustedOrigin(request: Request): boolean { const origin = request.headers.get("origin"); // Kein Origin-Header: moderne Browser senden ihn bei POST/PUT/DELETE sowohl // fuer Cross-Origin- als auch fuer Same-Origin-Anfragen mit, ein fehlender // Header stammt also nicht von einem regulaeren browserbasierten // Cross-Site-Angriff. Wird hier bewusst durchgelassen, um serverseitige/ // Tool-Aufrufe (z.B. Health-Checks) nicht zu blockieren. if (!origin) return true; try { const originHost = new URL(origin).host; const requestHost = new URL(request.url).host; if (requestHost === originHost) return true; const trustedHost = new URL(getBaseUrl()).host; return trustedHost === originHost; } catch { return false; } }