Zwei Minuten: Wie unser unsichtbares Captcha durchfiel — und was stattdessen hält
Der letzte Bericht hatte einen Absatz, auf den wir ein bisschen stolz waren: das unsichtbare Captcha. Der Browser rechnet im Hintergrund, der Mensch merkt nichts, der Roboter rechnet sich müde. Dazu ein Fangfeld und eine Mindest-Ausfüllzeit — drei stille Wächter. Am selben Abend haben wir uns hingesetzt und das Formular angegriffen, so wie es jemand tun würde, der es nicht gut mit uns meint.
Es hat zwei Minuten gehalten.
Was in zwei Minuten ging
Kein Exploit, keine Lücke im Framework — nur ein kleines Skript, das genau das tut, was der Browser auch tut. Das Ergebnis, ohne Anleitung, aber ehrlich:
- Leads aus dem Nichts. Erfundene Namen, erfundene Adressen, Adressen ohne Klammeraffe — alles landete als Lead im CRM. Ein Nachmittag Skript, und die Pipeline wäre voller Karteileichen gewesen.
- Fremde Postfächer. Das Formular bietet „Kopie per E-Mail senden“. Trägt man eine fremde Adresse ein, verschickt unser Mailserver brav eine Nachricht an jemanden, der nie gefragt hat — mit unserem Absender. Man nennt das Backscatter, und es ist der schnellste Weg, ein Absenderkonto auf schwarze Listen zu bringen.
- Text, wo keiner hingehört. Zeilenumbrüche in einem Namensfeld sind harmlos, bis sie in einer Mail-Kopfzeile landen. Dann werden aus einem Namen zwei Empfänger.
- Keine Bremse. Nichts hielt das Skript davon ab, das alles tausendmal zu tun.
Und die drei Wächter? Das Fangfeld lässt ein Skript einfach leer. Die Ausfüllzeit wartet es ab. Und das Rechenrätsel — das war der eigentliche Lerneffekt.
Die Rechnung, die wir nicht gemacht hatten
Ein Proof-of-Work-Captcha lebt von einer Behauptung: Für den Menschen ist die Rechenaufgabe kurz, für den Angreifer in Summe teuer. Wir haben die Schwierigkeit durchgerechnet — und die Behauptung stimmt nicht. Jede Stufe, die ein Mensch am Handy noch erträgt, kostet eine Grafikkarte nichts. Man kann die Aufgabe so schwer machen, dass sie einen Spammer bremst; dann wartet der ehrliche Besucher eine Minute auf den Absenden-Knopf. Es gibt keine Einstellung, bei der beides gilt.
Dahinter steckt eine allgemeinere Einsicht, und sie ist der Kern dieses Berichts: Alles, was im Browser läuft, gehört dem Angreifer. Rätsel, Fangfeld, Timer, Cookie, auch das Bilderraten eines Drittanbieters — es sind Hürden, die der Client nehmen soll. Aber der Client ist beim Angriff nicht unser Browser, sondern sein Skript. Er kann jede Hürde nachbauen, weil er sie sehen kann.
Was er nicht nachbauen kann: einen Brief öffnen, der in ein Postfach gelegt wurde, das ihm nicht gehört.
Der einzige Faktor, der bleibt
Also haben wir das Formular umgedreht. Nicht mehr „beweise, dass du ein Mensch bist“, sondern „beweise, dass dir diese Adresse gehört“. Wer eine Anfrage stellt, bekommt einen sechsstelligen Code an genau die Adresse, die er eingetragen hat, und trägt ihn ein. Erst dann geht die Anfrage weiter. Das ist nicht neu — jede Registrierung im Netz macht es so — aber es ist das Einzige, was gegen ein Skript hält. Erfundene Adressen bekommen keinen Code. Fremde Adressen bekommen einen Code, den der Angreifer nicht lesen kann. Die Kopie-Mail geht nur noch an ein bestätigtes Postfach.
Damit es nicht lästig wird: Eine bestätigte Adresse wird sich für ein paar Stunden gemerkt, in einem signierten Cookie, den der Browser weder lesen noch fälschen kann. Wer drei Objekte hintereinander anfragt, tippt den Code einmal. Und die Merkung gilt nur für genau diese Adresse — wer nach der Bestätigung eine andere einträgt, fängt von vorn an.
Dazu ein Deckel: Pro Adresse sind nur wenige Codes gleichzeitig offen, damit niemand ein fremdes Postfach mit unseren Mails bombardieren kann. Und weil der Code selbst in eine Mail gehört, die der Makler gestalten will, ist er eine Vorlage der Poststelle wie alle anderen — mit Platzhaltern, Sprachvarianten und Archiv.
Ein Dienst, der nichts weiß
Der Teil, der uns am meisten Freude gemacht hat, ist unsichtbar. Der Bestätigungsdienst weiß nichts von Leads, Exposés oder Formularen. Er kann genau zwei Dinge: „Bestätige die Identität hinter Adresse X“ und „hier ist der Code, stimmt er?“. Wie er bestätigt — heute per E-Mail — ist ein austauschbarer Kanal, von dem der Rest nichts sieht. Das Formular ist nur ein Aufrufer unter mehreren.
Der Beweis dafür kam sofort: Die Registrierung eines Besucherkontos läuft jetzt über denselben Dienst. Vorher legte sie das Konto an und meldete es an, ohne dass je jemand geprüft hätte, ob die Adresse dem Registrierenden gehört — man hätte Konten auf fremde Adressen anlegen können. Jetzt kommt zuerst der Code, und erst die bestätigte Adresse bekommt ein Konto. Keine neue Logik, kein zweiter Weg, nur ein zweiter Aufrufer.
Was drumherum dazugehört
Der Code ist der Kern, aber nicht die ganze Antwort. Drei Dinge flankieren ihn:
- Eine Bremse am Rand. Das Kontaktformular nimmt pro Absender-Adresse im Netz zwei Sendungen pro Minute an, Anmeldung und Registrierung fünf. Wer mehr will, bekommt eine höfliche Absage mit Wartezeit. Bewusst nicht ausgewertet werden dabei Kopfzeilen, mit denen sich ein Absender eine andere Adresse geben könnte — die Website spricht direkt mit dem Netz, also zählt die echte Verbindung.
- Prüfung auf beiden Seiten. Die Website prüft jedes Feld, bevor sie es weiterreicht: genau eine saubere Adresse, keine Zeilenumbrüche in einzeiligen Feldern, Längengrenzen. Und der Anwendungsserver prüft dasselbe noch einmal, weil die Website nicht die letzte Instanz ist.
- Die Absenderadresse an der richtigen Stelle. Sie hing bisher an der Immobilien-Komponente eines Menüeintrags. Jetzt gehört sie zur Website selbst — denn Codes, Anfragen und künftige Mails haben mit Immobilien nichts zu tun.
Zwei Dinge haben wir bewusst nicht gebaut. Die Anmeldung bekommt keinen zweiten Faktor; wer Adresse und Passwort kennt, kommt hinein, und die Bremse reicht gegen das Durchprobieren. Und die drei stillen Wächter sind komplett ausgebaut — nicht abgeschaltet, sondern gelöscht. Ein Schutz, der nicht schützt, ist kein Schutz, sondern eine Beruhigung.
Die Lehre
Der Bericht von gestern war nicht falsch: Das Formular tat, was es sollte, die Leads kamen an, die Vorlagen funktionierten. Er war nur nicht zu Ende gedacht. Wir hatten die Hürde gebaut, bevor wir den Angreifer gespielt hatten — und der Angreifer braucht zwei Minuten, um eine Hürde zu übersteigen, die er sehen kann.
Deshalb ist die Reihenfolge jetzt umgekehrt: erst angreifen, dann bauen. Und beim Bauen die Frage stellen, die den ganzen Unterschied macht — nicht „wie erschweren wir es“, sondern „was kann der Angreifer grundsätzlich nicht“. Die Antwort war ein Postfach. Sie wird es beim nächsten Formular wieder sein.
Die Poststelle hat inzwischen ihre erste Bestätigungsmail verschickt. Die zweite kam ein paar Sekunden später — an dieselbe Adresse, mit einem anderen Code, weil ein Tester nicht glauben wollte, dass der erste wirklich verbrannt war. Er war es.