Der Marktplatz war seit acht Monaten in Betrieb. Zwei Mitbegründer, keiner davon mit einem Sicherheitshintergrund, hatten ihn auf Bubble.io mit Stripe Connect für Zahlungen aufgebaut. Er hatte Transaktionen im Wert von über 40.000 US-Dollar verarbeitet, hatte 1.500 registrierte Benutzer und wuchs stetig durch Mundpropaganda.
Zwölf Minuten nach Beginn des Penetrify-Scans wurde ein kritisches Ergebnis gemeldet: Der geheime Stripe API-Schlüssel war in einer clientseitigen JavaScript-Datei eingebettet, die jedem Browser der Besucher bereitgestellt wurde. Er war seit dem Start dort. Vier Monate lang voller Lese-/Schreibzugriff auf ihr gesamtes Stripe-Konto, verfügbar für jeden, der die DevTools öffnete.
Dies ist die Geschichte, wie ein 40.000-Dollar-Unternehmen beinahe zu einer warnenden Geschichte wurde.
Der Stripe API-Schlüssel, den Sie niemals offenlegen sollten
Stripe vergibt zwei Arten von API-Schlüsseln: veröffentlichbare Schlüssel und geheime Schlüssel.
Der veröffentlichbare Schlüssel (pk_live_...) ist für die Verwendung im Frontend-Code konzipiert. Er kann nur begrenzte Operationen ausführen – das Erstellen von Zahlungsmethoden-Tokens, das Bestätigen von Zahlungen – und kann nicht auf sensible Kontodaten zugreifen. Es ist sicher, ihn in clientseitigem JavaScript offenzulegen.
Der geheime Schlüssel (sk_live_...) ist eine völlig andere Angelegenheit. Mit einem geheimen Stripe-Schlüssel können Sie:
- Alle Kunden, Zahlungsmethoden und Abonnements auflisten
- Vollständige Karten-Fingerabdrücke und Rechnungsadressen aller Kunden lesen
- Beliebige Rückerstattungen für jede Transaktion veranlassen
- Belastungen gegen jede gespeicherte Zahlungsmethode erstellen
- Auszahlungspläne und Bankkontodaten ändern oder löschen
- Auf alle Connected Accounts zugreifen (in diesem Fall alle Verkäufer-Zahlungskonten auf dem Marktplatz)
- Bankkontodaten für jeden Verkäufer abrufen, der sich über Stripe Connect registriert hatte
Der geheime Stripe-Schlüssel ist der Generalschlüssel zu Ihrer gesamten Zahlungsinfrastruktur. Er gehört nur in serverseitigen Code, in Umgebungsvariablen, die niemals an den Browser gesendet werden.
Wie er im Bundle landete
Bubble.io ist eine No-Code-Plattform, mit der Sie Full-Stack-Webanwendungen ohne Code schreiben können. Sie verfügt über ein integriertes Workflow-System für serverseitige Logik und einen API-Konnektor für externe Dienste. Für ein Zweierteam ohne technischen Hintergrund ist es eine legitime Möglichkeit, schnell ein echtes Produkt auf den Markt zu bringen.
Der geheime Stripe-Schlüssel landete aus einem Grund im Frontend, der jedem bekannt sein dürfte, der unter Zeitdruck auf einer No-Code-Plattform entwickelt hat: Es funktionierte.
Während der Entwicklung musste das Team Stripe API-Aufrufe aus Bubble-Workflows tätigen. Sie fügten den geheimen Schlüssel zur API-Konnektor-Konfiguration hinzu. Irgendwann in der Konfiguration – sei es ein Missverständnis der clientseitigen vs. serverseitigen Ausführung oder ein nicht offensichtliches Bubble-Konfigurationsdetail – wurde der Schlüssel in die an den Browser gesendete JavaScript-Nutzlast aufgenommen. Die Zahlungsabläufe funktionierten. Die Transaktionen wurden korrekt verarbeitet. Es gab keinen Fehler, keine Warnung, kein offensichtliches Symptom.
Die Bubble-Plattform handhabt viele serverseitige Ausführungen standardmäßig korrekt, aber die Grenze zwischen dem, was im Browser und dem, was auf Bubbles Servern läuft, ist im No-Code-Builder nicht immer visuell offensichtlich. Dies ist ein gut dokumentiertes Risiko in der Bubble-Entwickler-Community, aber es ist leicht zu übersehen, wenn man sich darauf konzentriert, das Produkt zum Laufen zu bringen, anstatt zu prüfen, welche Daten Ihre App an den Client sendet.
Was die Offenlegung tatsächlich bedeutete
Seien wir spezifisch, was in diesen vier Monaten auf dem Spiel stand.
Jeder, der den Marktplatz besuchte, konnte die Chrome DevTools öffnen, zum Tab „Sources“ oder „Network“ navigieren, die JavaScript-Dateien nach sk_live_ durchsuchen und den Schlüssel finden. Dies erfordert keine Hacking-Tools, kein spezielles Wissen und keine Schwachstelle über die Fähigkeit hinaus, mit der rechten Maustaste zu klicken und eine Webseite zu inspizieren.
Mit diesem Schlüssel hätten sie:
- Den Stripe-Kontostand des Unternehmens geleert, indem sie Rückerstattungen für abgeschlossene Transaktionen vorgenommen und bereits erzielte Einnahmen rückgängig gemacht hätten.
- Alle Kundendatensätze aufgelistet, einschließlich Namen, E-Mail-Adressen, teilweiser Kartendaten und Rechnungsadressen – ein meldepflichtiger Datenverstoß gemäß GDPR und verschiedenen US-amerikanischen Datenschutzgesetzen.
- Auf Bankkontodaten von Verkäufern zugegriffen, für jeden Verkäufer, der sein Auszahlungskonto über Stripe Connect verbunden hatte – Namen, Kontonummern, Bankleitzahlen.
- Auszahlungspläne geändert, um Verkäuferauszahlungen auf von Angreifern kontrollierte Konten umzuleiten.
- Betrügerische Abbuchungen vorgenommen gegen jede gespeicherte Kunden-Zahlungsmethode, bis zu den Stripe-Limits für das Konto.
Jedes dieser Ergebnisse hätte das Geschäft beendet. Zusammen stellen sie die Art von katastrophalem Verstoß dar, die in Vorträgen auf Sicherheitskonferenzen thematisiert wird.
Der Stripe-Schlüssel war 4 Monate lang offengelegt. In dieser Zeit hatten etwa 8.000 Personen den Marktplatz besucht. Jeder von ihnen hätte ihn finden können.
Die weiteren Ergebnisse
Der Stripe-Schlüssel war das schwerwiegendste Ergebnis, aber der Scan deckte vier weitere Probleme auf:
MITTEL — Bubble-Datenschutzregeln falsch konfiguriert
Die Datenschutzregeln von Bubble steuern, welche Datenbankfelder für verschiedene Benutzerrollen sichtbar sind. Die Verkäuferprofil-Datensätze – die Bankkontodaten enthielten, die während des Stripe Connect-Onboardings eingegeben wurden – waren für jeden authentifizierten Benutzer über die Daten-API von Bubble sichtbar. Selbst ohne den Stripe-Schlüssel hätte jeder eingeloggte Käufer Finanzinformationen von Verkäufern abfragen können.
MITTEL — Konto-Enumeration über Passwort-Reset
Der Passwort-Reset-Flow lieferte unterschiedliche Antworten für registrierte und nicht registrierte E-Mail-Adressen. Eine Anfrage für eine registrierte E-Mail-Adresse ergab „Posteingang prüfen“; eine Anfrage für eine nicht registrierte E-Mail-Adresse ergab „Kein Konto gefunden“. Dies ermöglicht einem Angreifer, eine Liste der E-Mail-Adressen zu erstellen, die Konten auf der Plattform haben – nützlich für gezieltes Phishing oder Credential Stuffing.
MITTEL — Keine Content Security Policy
Die Anwendung lieferte keinen Content-Security-Policy-Header. Die Suchfunktion spiegelte in einigen Kontexten benutzerdefinierte Eingaben ohne Kodierung wider, was reflektiertes XSS ermöglichte. Ohne eine CSP könnte eine XSS-Payload Session-Tokens exfiltrieren, authentifizierte API-Aufrufe im Namen des Opfers durchführen oder bösartige Skripte für andere Besucher in die Seite injizieren.
NIEDRIG — CORS-Wildcard
Die API lieferte Access-Control-Allow-Origin: * zurück, was jeder Website ermöglicht, Cross-Origin-Anfragen an die API-Endpunkte zu stellen und die Antworten zu lesen. Für Endpunkte, die sensible Daten zurückgeben, ermöglicht dies die Cross-Site-Datenexfiltration von einer bösartigen Seite, die das Opfer besucht.
Die Reaktion: Sofort und effektiv
Die Gründer handelten innerhalb einer Stunde nach Erhalt des Berichts.
Der erste Schritt war die Rotation des kompromittierten Stripe-Schlüssels. Im Stripe-Dashboard, unter Developers → API Keys, kann der Live-Secret-Key rotiert werden – dabei wird ein neuer Schlüssel generiert und der alte sofort für ungültig erklärt. Dies dauerte etwa zwei Minuten. Von diesem Moment an konnte niemand mehr den offengelegten Schlüssel verwenden, der ihn extrahiert hatte.
Der zweite Schritt war die Überprüfung der Event-Logs von Stripe auf unautorisierte Aktivitäten. Das Dashboard von Stripe bietet ein vollständiges Protokoll jedes API-Aufrufs, der mit Ihren Schlüsseln getätigt wurde, einschließlich der IP-Adresse und des Zeitstempels jeder Anfrage. Die Gründer überprüften die Event-Logs der letzten vier Monate auf anomale Aufrufe – Rückerstattungen, die sie nicht veranlasst hatten, Kunden, die sie nicht erstellt hatten, Auszahlungsänderungen, die sie nicht vorgenommen hatten. Sie fanden keine. Der Schlüssel war offengelegt worden, aber – soweit feststellbar – nicht aktiv missbraucht worden.
Der dritte Schritt bestand darin, die Bubble-Konfiguration zu korrigieren. In Zusammenarbeit mit einem Bubble-Entwickler, den sie für einige Stunden engagiert hatten, verlagerten sie alle Stripe API-Aufrufe in die serverseitigen Backend-Workflows von Bubble, wo API-Schlüssel nicht an den Browser übertragen werden. Die Bubble-Datenschutzregeln wurden ebenfalls korrigiert, um die Finanzdaten der Verkäufer nur auf das jeweilige Verkäuferkonto zu beschränken.
So finden Sie dies in Ihrer eigenen Anwendung
Wenn Sie eine Bubble-, Webflow- oder eine andere No-Code-Anwendung betreiben, die mit Stripe integriert ist, können Sie wie folgt überprüfen, ob Sie dieses Problem haben:
- Öffnen Sie Ihre Anwendung in einem Inkognito-Fenster.
- Öffnen Sie die Chrome DevTools (F12) → Registerkarte „Netzwerk“.
- Laden Sie die Seite neu.
- Suchen Sie auf der Registerkarte „Netzwerk“ nach JavaScript-Dateien. Klicken Sie auf jede einzelne und suchen Sie (Strg+F) nach
sk_live_. - Überprüfen Sie auch die Registerkarte „Quellen“. Verwenden Sie Strg+Umschalt+F, um in allen geladenen Skripten nach
sk_live_zu suchen.
Wenn Sie Ihren geheimen Stripe-Schlüssel in einer dieser Dateien finden, rotieren Sie ihn sofort, bevor Sie etwas anderes tun, und untersuchen Sie dann, wie er dorthin gelangt ist.
Dieselbe Überprüfung gilt für jeden anderen sensiblen API-Schlüssel: OpenAI, Twilio, SendGrid, AWS, Mailchimp. Jeder Schlüssel mit Schreibzugriff oder Zugriff auf sensible Daten, den Sie in clientseitigem JavaScript finden, sollte als kompromittiert betrachtet und sofort rotiert werden.
Warum dieses Muster fortbesteht
Die Offenlegung geheimer Schlüssel in Frontend-Bundles ist keine neue Schwachstellenklasse. Es ist ein bekanntes, gut dokumentiertes Risiko, solange Webanwendungen Drittanbieter-APIs verwenden. Warum geschieht es also immer wieder?
Die Antwort ist, dass die Entwicklungserfahrung es einfach macht. No-Code-Plattformen und moderne JavaScript-Frameworks verwischen die Grenze zwischen Client und Server auf eine Weise, die in früheren Webentwicklungsmodellen nicht vorhanden war. Umgebungsvariablen, die mit NEXT_PUBLIC_ präfixiert sind, werden absichtlich an den Browser gesendet; solche ohne Präfix nicht. Der Ausführungskontext von Bubble hängt davon ab, welchen Workflow-Typ Sie verwenden. Vite- und webpack-Bundle-Konfigurationen bestimmen, was im Browser landet.
Diese Grenzen sind dokumentiert, werden aber auf Tooling-Ebene nicht erzwungen. Es gibt keinen Build-Time-Fehler, wenn Sie versehentlich einen geheimen Schlüssel offenlegen. Die Anwendung funktioniert korrekt. Die Offenlegung ist still, unbegrenzt und wächst jeden Tag, an dem der Schlüssel gültig bleibt.
Die einzige zuverlässige Verteidigung besteht darin, explizit danach zu suchen – und schnell zu rotieren, wenn Sie es finden.
