Texte
Wie ich fünf API-Schwachstellen mit einem Prompt und Claude Fable 5 behob
Hinter dem VLOP-Transparenz-Dashboard steckt ein kleiner FastAPI-Dienst — eine strukturierte Query-API nach dem Vorbild der TikTok Research API, die aggregierte EU-DSA-Transparenzdaten bereitstellt. Mehrere ihrer Endpunkte sind bewusst öffentlich: ein interaktiver Query-Builder, ein „Ask"-Feld für Fragen in natürlicher Sprache, ein Überblicks-Feed. Öffentliche Endpunkte eines Diensts im Hobby-Maßstab sind genau die Stelle, an der sich Sicherheitsschulden leise ansammeln, weil einen nichts zwingt hinzuschauen.
Also bat ich Claude Code — mit Fable 5, dem ersten Modell in Anthropics Claude-5-Familie — hinzuschauen. Der Prompt war eine Zeile: „Führe eine Sicherheitsprüfung über die API durch und behebe etwaige Schwachstellen." Zurück kam ein Pull Request mit fünf eigenständigen Fixes, jeder mit Regressionstests, plus einem Self-Review des eigenen Diffs.
Was es fand
Eine SSRF-Lücke in Webhook-Callbacks. Die API lässt eine Query eine callback_url mitführen, die per POST angesprochen wird, sobald der Job fertig ist — eine klassische Server-Side-Request-Forgery-Angriffsfläche. Der bestehende Guard blockierte private, Loopback-, Link-Local- und Cloud-Metadaten-Bereiche, aber eine Blockliste ist die falsche Form für dieses Problem: Sie übersah Bereiche, die weder privat noch öffentlich sind, wie Carrier-Grade NAT (100.64.0.0/10). Der Fix dreht die Prüfung um — ein Callback-Ziel muss nun global routbar sein, sonst wird es abgelehnt.
CSV-Formel-Injection. Query-Ergebnisse lassen sich als CSV exportieren, und der Datensatz enthält Freitextfelder aus fremden Transparenzberichten. Eine Zelle, die mit =, +, - oder @ beginnt, wird als Formel ausgeführt, wenn die Datei in Excel oder Sheets geöffnet wird — was bedeutet, dass ein Wert im veröffentlichten Bericht eines anderen Unternehmens Code auf dem Rechner eines Forschers ausführen könnte. Textzellen mit diesen Präfixen werden nun neutralisiert, sowohl serverseitig als auch im clientseitigen Export des Dashboards.
Unbegrenzte Request-Bodies und Query-Komplexität. Der öffentliche Query-Endpunkt akzeptiert das vollständige strukturierte Query-Modell ohne Authentifizierung. Nichts begrenzte, wie groß ein Request-Body sein oder wie viele Bedingungen eine Query stapeln durfte. Bodies über einem Limit werden nun mit einem 413 abgelehnt, bevor irgendeine Parsing-Arbeit beginnt, und das Query-Modell selbst begrenzt Werte pro Bedingung, Bedingungen pro Klausel und Felder pro Request.
Ein Timing-Seitenkanal bei der API-Key-Prüfung. Keys wurden per Dictionary-Lookup geprüft, das beim ersten abweichenden Zeichen abbricht — im Prinzip verrät das Antwort-Timing Key-Präfixe. Der Vergleich läuft nun in konstanter Zeit über jeden konfigurierten Key.
Plus diverse Härtungen — die Art kleinteiliger Punkte, die ein Checklisten-Durchgang aufspürt und ein vielbeschäftigter Maintainer übersieht: Grenzen für Paginierungsparameter, solche Dinge.
Der Teil, der mich überraschte
Der Pull Request löste ein automatisches Review durch eine zweite KI aus, Gemini Code Assist, das zwei Vorschläge hinterließ. Fable 5 wertete beide anhand des tatsächlichen Quellcodes aus, bevor es handelte — und bei einem davon fand es die Begründung des Bots unvollständig. Gemini merkte an, dass das Parsen eines vom Angreifer gelieferten Content-Length-Headers beliebiger Länge in einen Integer CPU verbrennen könne. Zutreffend, aber nebensächlich. Das schärfere Problem, das Fable 5 identifizierte und in seiner Antwort erläuterte, ist, dass Python 3.11+ sich weigert, Integer über ~4.300 Stellen zu parsen, und stattdessen eine Exception wirft — sodass ein pathologischer Header einen 500-Fehler statt des beabsichtigten 413 erzeugt hätte. Es verallgemeinerte außerdem Geminis hartkodierten Fix, sodass die Grenze dem konfigurierten Limit folgt, und fügte einen Regressionstest hinzu, der einen 5.000-stelligen Header sendet. Eine KI, die das Review einer anderen KI über den Code der ersten KI reviewt und in der Meinungsverschiedenheit recht behält — das stand nicht auf meiner Bingo-Karte für 2026.
Was ich daraus mitnehme
Das reimt sich auf das, was ich über LLMs in der Compliance-Arbeit geschrieben habe: Die Erfolge kommen bei strukturierten Aufgaben mit verifizierbaren Ergebnissen. Ein Sicherheits-Durchgang durch eine kleine Codebasis ist genau das — eine endliche Checkliste (Injection-Flächen, SSRF, Seitenkanäle, Ressourcenerschöpfung), erschöpfend angewandt, wobei sich jeder Fund durch Lesen des Codes bestätigen und jeder Fix mit einem Test festnageln lässt. Erschöpfend-und-langweilig ist das Terrain, auf dem Modelle jetzt müde Menschen zuverlässig schlagen.
Das Urteil blieb bei mir. Die Entscheidung, dass der Metrics-Endpunkt unauthentifiziert bleiben darf, dass Demo-Keys außerhalb der Produktion akzeptabel sind, dass ein In-Memory-Job-Store für einen Demo-Dienst genügt — das sind Threat-Model-Entscheidungen darüber, wofür der Dienst da ist, und das Modell tat nicht so, als wäre es anders. Der vollständige Diff, inklusive Tests, steht im Pull Request; die Suite läuft mit 122 Tests und ist grün.