So habe ich mein produktives Next.js-SaaS gegen SSRF und Stripe-Webhook-Angriffe abgesichert
März 2026 · Damir Andrijanic · 6 Min. Lesezeit
Wenn dein Produkt Nutzer auffordert, "irgendeine URL zum Scannen" einzufügen, öffnest du deinem Server praktisch die Eingangstür zum Internet.
Beim Bau von ComplianceRadar wurde mir schnell klar: Rohe URL-Inputs sind kein nettes UX-Feature, sondern eine harte Sicherheitsgrenze.
Die Plattform läuft auf Next.js App Router, serverlosen API-Routen, Stripe und KI-basierten Analyse-Pipelines. Weil beliebige URLs akzeptiert werden, musste Security vom ersten Tag an Teil der Architektur sein.
SSRF-Risiko kontrollieren
Behandle jede eingereichte URL als potenziell feindlichen Input.
Outbound-Requests absichern
Protokoll-Checks, IP-Filter, Timeouts und Payload-Limits sind Pflicht.
Integrität von Zahlungsevents
Stripe-Signaturen am Raw-Body prüfen, bevor Features freigeschaltet werden.
Produktions-Stack im Kontext
- Next.js (App Router)
- Serverlose API-Routen
- Stripe für Zahlungen
- KI-basierte Analyse-Pipelines
1) Die SSRF-Gefahr: Vertraue URLs nie blind
Server-Side Request Forgery (SSRF) entsteht, wenn Angreifer dein Backend dazu bringen, interne oder geschützte Netzwerkziele aufzurufen.
Statt einer normalen Ziel-URL schicken sie lokale oder Metadaten-Endpunkte und missbrauchen deinen Server als Proxy.
http://localhost:3000/api/admin-delete-allhttp://169.254.169.254/latest/meta-data/
Wenn dein Backend alles ungeprüft abruft, ist deine externe Firewall nicht mehr deine echte Schutzgrenze.
Um das zu verhindern, habe ich vor jedem Outbound-Request eine strikte Validierungs- und Sanitizing-Pipeline eingebaut.
Protokoll-Allowlist
- Nur http:// und https:// zulassen
- file://, ftp://, gopher://, data:// sofort ablehnen
- Early fail, bevor irgendein Netzwerk-Call startet
Private IPs und geschützte Ranges blocken
Vor dem Fetch wird der Hostname aufgelöst. Zeigt die IP auf private oder sensible Bereiche, wird der Request sofort abgebrochen:
- 127.0.0.0/8
- 10.0.0.0/8
- 192.168.0.0/16 und 172.16.0.0/12
- 169.254.0.0/16 (Cloud-Metadaten-Endpunkte)
- Alle localhost-äquivalenten internen Ziele
Timeouts und Payload-Limits
Jeder Outbound-Request läuft mit 10-Sekunden-AbortController-Timeout und Größenlimits, damit langsame Antworten oder riesige Downloads keine DoS-Angriffsfläche werden.
2) Umsatz schützen: Stripe-Webhooks härten
Nachdem die Scan-Engine abgesichert war, lag der nächste kritische Fokus auf dem Zahlungsfluss.
Wenn ein Nutzer bezahlt, sendet Stripe ein Event an /api/webhooks/stripe. Ein häufiger Anfängerfehler ist, das eingehende JSON direkt zu vertrauen und Premium sofort freizuschalten.
Das ist unsicher. Jeder kann einen POST auf diesen Endpoint schicken. Vertrauenswürdig ist nur ein Payload, der die Stripe-Signaturprüfung besteht.
stripe-signatureconst body = await req.text()
Der entscheidende Punkt im Next.js App Router: zuerst den rohen Body mit req.text() lesen, erst danach weiterverarbeiten. Stripe signiert die exakten Roh-Bytes. Wenn du vorher JSON parst, schlägt die Verifikation fehl.
Referenz-Implementierung
Minimales Muster zur Webhook-Verifikation im Next.js App Router.
import { headers } from "next/headers";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const body = await req.text();
const signature = headers().get("stripe-signature");
if (!signature) return new Response("Unauthorized", { status: 400 });
try {
stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);
} catch {
return new Response("Unauthorized", { status: 400 });
}
// Handle verified event
return new Response("ok");
}Security ist Architektur, nicht Checkbox
SSRF-Blockaden halten deine Scan-Engine isoliert von interner Infrastruktur und Cloud-Metadaten-Services.
Die Verifikation der Stripe-Signatur schützt Billing-Logik und Feature-Freischaltung vor gefälschten Zahlungsevents.
Ein großer Teil von produktionsreifer Software ist unsichtbar. Es ist der Code, der teure Ausfälle verhindert, bevor sie passieren.
Threat-Model-Checkliste (Ja/Nein)
Kurzer Selbstcheck, bevor du dein SaaS als produktionsreif bezeichnest:
- 01Protokoll-Whitelist: Blockiert deine App strikt alle Nicht-HTTP/HTTPS-Schemes (z. B. file:// oder ftp://) bei Nutzer-Inputs?
- 02Private-IP-Blocking: Löst du eingereichte Domains auf und blockierst interne IP-Ranges (z. B. 127.x.x.x, 169.254.x.x), bevor du fetchst?
- 03Outbound-Constraints: Sind alle serverseitigen Fetch-Requests in einen AbortController mit strikten Timeouts und Payload-Limits eingebettet?
- 04Webhook-Integrität: Verifizierst du Stripe-Webhook-Signaturen mit dem Raw-Body (req.text()), bevor JSON geparst wird?
- 05Secret-Management: Liegen Webhook-Signing-Secrets und API-Keys sicher serverseitig und niemals mit NEXT_PUBLIC_-Präfix?
Wenn du das Projekt hinter diesen Entscheidungen sehen willst, schau dir ComplianceRadar an.
Live-Projekt: complianceradar.dev ↗
Wenn du gerade ein SaaS baust: Prüfe deine URL-Inputs und Webhook-Endpunkte lieber heute als nach dem ersten Incident.