Alle Beiträge
Vorfall6 Min. Lesezeit

Ein einziges Anbieter-Skript kostete Polymarket-Nutzer 2,5 Mio. $. Prüf deine heute

Browser window with a row of puzzle-piece plugins, one glowing orange piece pulling data streams out of a safe

Letzten Donnerstag, am 29. Oktober, schaltete Polymarket seine Börse in den Cancel-only-Modus. Nutzer meldeten geleerte Konten, und innerhalb von etwa einer Stunde bestätigte das Unternehmen die Ursache, in eigenen Worten: „Einer unserer Drittanbieter wurde kompromittiert“, wie The Block zitiert.

Der Anbieter war laut The Wall Street Journal, via Finance Magnates, DebugBear, ein Performance-Monitoring-Dienst, dessen Code auf der Website von Polymarket lief. Eine in dem Bericht zitierte Blockchain-Analyse verfolgte mehr als 2,5 Millionen Dollar, die über mehr als 900 Transaktionen an eine dem Angreifer zugeordnete Wallet flossen. Polymarket sagt, betroffene Nutzer werden voll entschädigt.

Kein Passwort wurde geleakt. Keine Seed-Phrase wurde abgephisht. Die Nutzer waren einfach auf einer Website eingeloggt, die eine JavaScript-Datei vom Server eines anderen lud, und diese Datei hatte sich verändert.

Wenn deine App Analytics, Chat-Widgets, A/B-Tests, Session Replay oder Performance-Monitoring von einem fremden CDN lädt, vertraust du diesen Anbietern alles an, was dein eigener Code kann. Hier erfährst du, was passiert ist, warum es dich betrifft, auch wenn du keine Börse betreibst, und wie du deine eigene App noch diese Woche prüfst.

Was passiert ist

  • Die Website von Polymarket lud ein Real-User-Monitoring-Skript von DebugBears CDN. Ein ganz normales Setup. Tausende Websites machen dasselbe mit Dutzenden Anbietern.
  • Der Angreifer ersetzte diese Datei durch bösartiges JavaScript. Weil der Browser sie jedes Mal frisch beim Anbieter abrief, bekam jeder Besucher sofort die neue Version.
  • Das Skript prüfte, ob eine aktive Polymarket-Session bestand, las Kontodaten, Wallet-Details und Trading-API-Zugangsdaten aus dem Browser-Speicher und nutzte dann Polymarkets eigenen Signierablauf, um Positionen zu verschieben oder Bestände an eine Wallet des Angreifers zu verkaufen.

Das Detail, über das man nachdenken sollte: Laut den Forschern halfen weder Hardware-Wallets noch starke Passwörter, denn das Skript brauchte nie einen privaten Schlüssel. Es handelte als eingeloggter Nutzer, direkt aus dessen eigener Browser-Session heraus.

Polymarkets Fix war laut demselben Bericht ein Produktions-Commit, der das Anbieter-Skript entfernte. Der Handel lief noch am selben Nachmittag wieder an. SafeState merkt außerdem an, dass dies nicht der erste Vorfall mit einem Anbieter-Skript war, der die Plattform dieses Jahr traf.

Warum das für deine App wichtig ist

Ein Script-Tag, das auf cdn.some-vendor.com zeigt, läuft mit denselben Rechten wie dein eigener Code. Es kann das DOM lesen, localStorage und sessionStorage auslesen, deine API mit den Zugangsdaten des Nutzers aufrufen und ändern, was die Seite anzeigt. Zwischen „deinem Code“ und „dem Analytics-Snippet, das du in der Launch-Woche eingefügt hast“ gibt es keine Sandbox.

Das gilt besonders für schnell gelaunchte und KI-gebaute Apps, denn:

  • Skripte sammeln sich an. Jeder Prompt wie „füg Analytics hinzu“, „füg eine Chat-Bubble hinzu“ oder „füg Error-Tracking hinzu“ bringt meist ein weiteres Tag mit. Kaum jemand entfernt sie später wieder.
  • Tokens liegen im Browser-Speicher. Viele Starter-Templates legen das Session- oder API-Token in localStorage ab, wo jedes Skript auf der Seite es lesen kann.
  • Meist gibt es keine Content-Security-Policy. Ohne sie kann jedes eingeschleuste oder ausgetauschte Skript weiteren Code von überall nachladen und Daten überallhin senden.

Die Build-Pipelines deiner Anbieter kannst du nicht prüfen. Aber du kannst begrenzen, wie viel Schaden ein bösartiges Skript anrichten kann.

So prüfst du deine App

1. Liste jedes Skript auf, das deine Seiten laden. Öffne deine App, dann die DevTools, den Network-Tab, filtere nach JS und lade neu. Oder führe dies in der Konsole auf jeder wichtigen Seite aus:

[...document.scripts].map(s => s.src || '(inline)').filter(Boolean)

Notiere jede Domain, die nicht dir gehört. Mach das auch auf deinen Seiten hinter dem Login, nicht nur auf der Marketing-Website. Tag-Manager können weitere Skripte nachladen, die gar nicht in deinem HTML-Quelltext auftauchen.

2. Prüfe, welche dieser Tags ein integrity-Attribut haben. In der Konsole:

[...document.querySelectorAll('script[src]')]
  .filter(s => !s.src.startsWith(location.origin))
  .map(s => ({ src: s.src, sri: !!s.integrity }))

3. Sieh dir deine Response-Header an. Führe curl -sI https://yourapp.com aus und such nach Content-Security-Policy. Fehlt sie oder enthält sie script-src * oder 'unsafe-inline' ohne Nonces, schränkt sie nicht viel ein.

4. Prüfe, wo deine Session liegt. Schau in den DevTools unter Application bei Local Storage und Session Storage nach. Siehst du dort ein Access-Token, ein Refresh-Token oder einen API-Key, kann jedes Skript auf der Seite es lesen, und genau das hat der Polymarket-Payload getan.

So behebst du es

Entferne, was du nicht brauchst. Das ist der wirkungsvollste Schritt. SafeStates Empfehlungen für Betreiber beginnen damit, ein Inventar der Drittanbieter-Skripte zu führen und nicht notwendige Tags von Konto-, Checkout- und Transaktionsseiten zu entfernen. Deine Login-, Abrechnungs- und Einstellungsseiten brauchen selten ein Heatmap-Tool.

Pinne und verifiziere, was du behältst, mit Subresource Integrity (SRI). Wenn ein Anbieter versionierte Dateien anbietet, referenziere eine bestimmte Version und füge einen Hash hinzu. Ändert sich die Datei, verweigert der Browser die Ausführung:

<script
  src="https://cdn.example-vendor.com/sdk/4.2.1/sdk.min.js"
  integrity="sha384-REPLACE_WITH_REAL_HASH"
  crossorigin="anonymous"></script>

Erzeuge den Hash selbst aus genau der Datei, die du geprüft hast:

curl -s https://cdn.example-vendor.com/sdk/4.2.1/sdk.min.js \
  | openssl dgst -sha384 -binary | openssl base64 -A

Beachte den Kompromiss: SRI bricht Skripte, die Anbieter unter einer unversionierten URL direkt aktualisieren, was bei Monitoring- und Analytics-Snippets üblich ist. Für diese hostest du entweder eine gepinnte Kopie selbst, die du bewusst aktualisierst, oder du akzeptierst, dass du der Live-Datei des Anbieters vertraust, und hältst sie von sensiblen Seiten fern.

Füge eine Content-Security-Policy hinzu. CSP sagt dem Browser, welche Origins Skripte ausliefern dürfen und an welche Origins die Seite Daten senden darf. Ein Ausgangspunkt für eine Next.js- oder Express-App könnte so aussehen:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.example-vendor.com;
  connect-src 'self' https://api.yourapp.com;
  frame-ancestors 'none';
  base-uri 'self';
  object-src 'none'

Sei realistisch, was CSP dir hier bringt: Wenn die Domain des Anbieters selbst erlaubt ist, hält CSP eine bösartige Datei von dort nicht auf. CSP verhindert aber, dass dieses Skript Code von neuen Domains nachlädt oder gestohlene Daten an beliebige Endpunkte sendet, die nicht in connect-src stehen. Roll sie zuerst mit Content-Security-Policy-Report-Only aus, damit du siehst, was kaputtgeht. Unser Guide zum Einrichten einer Content-Security-Policy führt dich Schritt für Schritt durch.

Content-Security-Policy: der vollständige Einrichtungs-Guide

Bring Session-Tokens außer Reichweite von JavaScript. Verschiebe Session-Tokens in Cookies mit HttpOnly, Secure und SameSite, damit Seitenskripte sie nicht lesen können. Das hält ein bösartiges Skript nicht davon ab, Anfragen im Namen des Nutzers zu senden, solange die Seite offen ist, aber es verhindert, dass Tokens abgegriffen und anderswo wiederverwendet werden. Siehe Cookie-Flags Secure, HttpOnly und SameSite.

Halte deine eigenen Frontend-Keys sauber. Wenn ein Skript dein Bundle lesen kann, kann es jedes Secret lesen, das du darin ausgeliefert hast, und Bots jagen bereits nach geleakten Keys und .env-Dateien. Lies unseren Guide zu API-Keys im clientseitigen JavaScript, stell sicher, dass du keine Source Maps in Produktion auslieferst, und falls du schon einen Schreck hattest, zeigt unser Beitrag zum Rotieren der Secrets deiner App ohne Ausfallzeit, wie du aufräumst.

Secure, HttpOnly und SameSite: so sicherst du deine Session-Cookies ab

Checkliste zum Mitnehmen

Bevor du die DevTools schließt, stell sicher, dass du:

  • jedes Drittanbieter-Skript auf deinen öffentlichen und eingeloggten Seiten erfasst hast.
  • Skripte entfernt hast, die du nicht mehr nutzt, besonders auf Login-, Abrechnungs- und Kontoseiten.
  • Versionen gepinnt und Integrity-Hashes hinzugefügt hast, wo Anbieter das unterstützen.
  • Skripte, die sich nicht pinnen lassen, selbst hostest oder von sensiblen Seiten fernhältst.
  • eine Content-Security-Policy mit strengem script-src und connect-src auslieferst.
  • Session-Tokens in HttpOnly-Cookies statt in localStorage aufbewahrst.
  • dieses Audit wiederholst, sobald jemand (oder ein KI-Agent) ein neues Tag hinzufügt.

Finde heraus, wo du stehst

Wenn du schnell eine Ausgangsbasis willst: AppSafe prüft deine Security-Header, einschließlich der Frage, ob eine Content-Security-Policy gesetzt ist, und der verifizierte vollständige Scan untersucht Frontend-Probleme wie Subresource Integrity und verwundbare JavaScript-Bibliotheken. Wie ein Report aussieht, siehst du im Beispiel-Report, oder kombiniere das mit unserer Pre-Launch-Checkliste für KI-gebaute Apps.

Polymarket hat die Ursache innerhalb von etwa einer Stunde nach dem Handelsstopp gefunden. Viele kleine Teams würden deutlich länger brauchen, um ein verändertes Skript überhaupt zu bemerken. Starte heute einen kostenlosen AppSafe-Scan deiner App und finde heraus, wo du stehst.

Diese Checkliste automatisch durchlaufen

Ein kostenloser Scan prüft deine App auf all diese Probleme und liefert dir für jeden Befund einen KI-Fix-Prompt.

Meine App gratis scannen