Blog / development

13.400 Adressen, je eine Anfrage: wie ein Scraper-Schwarm eine Website lahmlegte – und warum klassische Limits ihn nicht sehen

Ein Scraper verteilte seine Anfragen auf 13.400 Adressen und legte eine Website zehn Minuten lahm. Woran man so einen Schwarm erkennt und wie man ihn bremst.

Symbolbild: Auf einem belebten Platz marschiert eine Kolonne identisch grau uniformierter Gestalten durch eine Menge gewöhnlicher Passanten

Zehn Minuten offline – und kein Server war kaputt

Am Montag, dem 28. September, war die Website der internationalen Weinplattform VINUM am frühen Abend rund zehn Minuten nicht erreichbar. Das Monitoring schlug um 17:44 Uhr Alarm, vier Minuten später stand die Ursache fest.

Der Befund war auf den ersten Blick widersprüchlich: Der Server lief seit Tagen ohne Unterbrechung, die Festplatte war halb leer, 16 GB Arbeitsspeicher waren frei, alle Dienste liefen. Das letzte Deployment lag mehr als zwei Stunden zurück. Nichts war defekt. Die Website war schlicht vollständig damit beschäftigt, Anfragen zu beantworten, die kein Mensch gestellt hatte.

Ein Scraper – ein Programm, das Inhalte massenhaft absaugt – hatte die Seite überrannt. Ungewöhnlich war nicht die Menge, sondern die Form: Die Anfragen kamen von rund 13.400 verschiedenen Adressen. Fast jede davon meldete sich genau ein einziges Mal.

Die Herausforderung

Die Plattform war nicht ungeschützt. Im Frühjahr hatten wir beschrieben, wie wir Webserver gegen aggressive Scanner absichern: mit Begrenzungen pro Absender-Adresse im Webserver nginx und automatischen Sperren über fail2ban. Dazu kommt ein gemeinsames Kontingent für bekannte Crawler. Dieser Schwarm lief an allen drei Schutzschichten vorbei:

  • Die Begrenzung pro Adresse zählt, was eine einzelne Adresse tut. Sie greift, wenn ein Absender zu viele Seiten in zu kurzer Zeit abruft. Hier rief jeder Absender eine Seite ab. Es gab nichts zu zählen.
  • fail2ban sperrt Adressen, die auffallen. Eine Adresse, die einmal kommt und nie wieder, fällt nicht auf – und eine Sperre gegen sie käme ohnehin zu spät.
  • Das Crawler-Kontingent erkennt Crawler an ihrer Selbstauskunft oder an ihrer Herkunft aus Rechenzentren. Der Schwarm gab sich als gewöhnlicher Chrome-Browser auf einem Mac aus und kam über private Internetanschlüsse. Solche Anschlüsse vermieten spezialisierte Anbieter als Durchleitung; für den Server sieht jede Anfrage aus wie ein einzelner Besucher von zu Hause.

Dazu kam die Auswahl der Ziele. Der Schwarm rief nicht die Startseite ab, die aus dem Zwischenspeicher kommt, sondern gezielt die teuersten Seiten der Plattform: Detailseiten einzelner Degustationsnotizen, die Weinsuche in immer neuen Filterkombinationen und einzelne Bilder aus Bildstrecken. Jede dieser Seiten wird frisch aus der Datenbank berechnet.

Die Zahlen des Abends:

  • Bis 17:17 Uhr kamen von diesem Absender höchstens 6 Anfragen pro Minute.
  • Ab 17:20 Uhr waren es rund 300 pro Minute. Die Website wurde spürbar langsamer, blieb aber erreichbar.
  • Ab 17:43 Uhr waren es 1.000 bis 1.250 pro Minute. Alle PHP-Prozesse waren belegt, die Datenbank lief am Anschlag.
  • Von rund 13.900 Anfragen des Schwarms endete die Hälfte damit, dass die Gegenseite die Verbindung abbrach, bevor eine Antwort fertig war. Der Server rechnete also zu einem großen Teil für niemanden.

Um 17:51 Uhr ließ der Schwarm von selbst nach, eine Minute später antwortete die Website wieder normal. Das ist der unangenehmste Teil des Befunds: Die Erholung war nicht unser Verdienst. Die Frage war also nicht, wie man den Abend rettet, sondern was beim nächsten Mal passiert.

Unsere Lösung

1. Rückwärts lesen: Das Muster stand schon in den Protokollen

Der Schwarm kam nicht aus dem Nichts. Ein Blick in die Zugriffsprotokolle der Tage davor zeigte dieselbe Handschrift: am Sonntag in sieben einzelnen Stunden jeweils 700 bis 1.500 Anfragen, in der Nacht auf Montag rund 1.900 und 2.500, am Montagvormittag in einer Stunde gut 7.600. Die Website hat das alles verkraftet. Am Abend waren es dann rund 17.800 in einer Stunde – und das reichte.

Solche Probeläufe fallen nicht auf, solange man nach Ausfällen sucht. Sie fallen auf, wenn man regelmäßig fragt: Wer ruft eigentlich unsere teuersten Seiten ab, und wie verteilen sich diese Abrufe auf Adressen? Ein Verhältnis von fast eins zu eins zwischen Anfragen und Adressen ist bei Menschen selten. Besucher klicken sich durch mehrere Seiten.

2. Das Merkmal finden, das der Schwarm nicht verteilen kann

Tausende Adressen lassen sich mieten. Schwerer zu verteilen ist, was das Programm über sich selbst sagt. Alle rund 13.900 Anfragen trugen exakt dieselbe Browser-Kennung: einen Desktop-Chrome in einer Version, die rund zehn Hauptversionen hinter dem aktuellen Stand lag.

Chrome aktualisiert sich selbst, alle paar Wochen erscheint eine neue Hauptversion. Ein echter Desktop-Chrome, der zehn Versionen zurückliegt, ist die Ausnahme. Tausende davon, die alle im selben Moment dieselben Datenbankseiten abrufen, sind kein Zufall.

Für sich genommen ist keines der beiden Merkmale ein Beweis. Ein veralteter Browser ist nicht verboten, und wer sich für Degustationsnotizen interessiert, ist willkommen. Erst die Kombination trägt: veraltete Kennung und teure Seite.

3. Drosseln statt sperren

Die naheliegende Reaktion wäre gewesen, diese Kennung auszusperren. Wir haben uns bewusst dagegen entschieden und nutzen stattdessen ein Prinzip, das für bekannte Crawler bereits galt: ein gemeinsames Kontingent.

Der Webserver nginx begrenzt Anfragen üblicherweise pro Absender-Adresse. Der Schlüssel, nach dem gezählt wird, ist aber frei wählbar – das beschreibt die nginx-Dokumentation zur Anfragebegrenzung. Für Anfragen, die als Schwarm eingestuft sind, zählen wir nicht mehr pro Adresse, sondern alle zusammen in einen einzigen, knapp bemessenen Topf. Ob der Schwarm über 10 oder über 13.000 Adressen kommt, spielt dann keine Rolle mehr: Zusammen bekommt er nur noch wenige Seiten pro Sekunde. Was darüber hinausgeht, erhält sofort die Antwort „zu viele Anfragen” und erreicht weder PHP noch die Datenbank.

Der Unterschied zur Sperre zeigt sich im Irrtumsfall. Liegt die Einstufung einmal falsch, wird ein echter Besucher kurz langsamer bedient oder sieht eine Meldung, die nach wenigen Sekunden verschwindet. Ausgesperrt ist er nicht.

Eine Drosselung verzeiht den Irrtum. Eine Sperre nicht.

4. Die Regel so bauen, dass ein Kostümwechsel nicht reicht

Eine Regel, die genau eine Kennung trifft, hält genau bis zum nächsten Update des Scrapers. Deshalb gibt es zwei Ebenen:

  • Die exakte Kennung des Ausfalls landet auf jeder Seite im gemeinsamen Topf.
  • Jeder deutlich veraltete Desktop-Chrome landet dort, sobald er eine der drei teuren Seitenarten abruft. Auf allen anderen Seiten bleibt er ein gewöhnlicher Besucher.

Bewusst ausgenommen sind Mobilgeräte und andere Browser: Die Regel soll das beobachtete Muster treffen und nicht jeden, der eine ältere Software nutzt. Geprüfte Suchmaschinen bleiben ebenfalls außen vor – erkannt nicht an ihrer Kennung, sondern an ihrer nachweisbaren Herkunft. Warum uns dieser Unterschied wichtig ist, steht im Beitrag über den Honeypot, der nie zuschnappte.

Die Schwelle für „deutlich veraltet” haben wir nicht geschätzt, sondern aus den Protokollen abgelesen: Welche Versionen nutzen die echten Besucher der Plattform tatsächlich, und wo beginnt das Muster „fast jede Anfrage von einer anderen Adresse”? Die Schwelle ist ein fester Wert. Mit der Zeit erfasst sie eher weniger als mehr und trifft dadurch nie versehentlich aktuelle Browser. Der Preis: Sie muss nachgezogen werden. Eine neuere Version zeigte bereits dasselbe Muster in klein und ist als nächster Kandidat vermerkt.

Auch hier gehört die Kehrseite dazu. Es gibt Rechner, die auf einem alten Betriebssystem festsitzen und deshalb keinen aktuellen Chrome mehr bekommen. Wer so einen Rechner nutzt, kann auf diesen drei Seitenarten kurz eine Meldung sehen, während gerade eine Welle läuft. Das haben wir dem Kunden so gesagt.

5. Beweisen, dass die Regel greift – und dass sie vorher nicht griff

Eine Drosselung sieht man der Konfiguration nicht an. Zur Änderung gehört deshalb ein Testskript, das echte Anfragen durch einen Webserver mit genau dieser Konfiguration schickt. Die entscheidende Probe bildet die Form des Ausfalls nach: 60 gleichzeitige Anfragen von 60 verschiedenen Adressen.

  • Mit der Kennung des Ausfalls: 21 beantwortet, 39 abgewiesen.
  • Mit einem aktuellen Chrome: 60 von 60 beantwortet.
  • Dieselbe Probe gegen die alte Konfiguration: 60 von 60 beantwortet – auch für den Schwarm. Das ist der Ausfall im Kleinen, nachgestellt auf dem Prüfstand.

Die Gegenprobe ist der wichtigere Teil. Ein Test, der nur zeigt, dass die neue Regel funktioniert, beweist nicht, dass er das Problem überhaupt messen kann. Insgesamt deckt das Skript 20 Fälle ab: gefälschte Suchmaschinen, echte Suchmaschinen, veraltete und aktuelle Versionen, teure und gewöhnliche Seiten.

Eine Kleinigkeit am Rand, die im Betrieb unangenehm geworden wäre: Die vollständige Kennung als gewöhnlicher Eintrag in der Zuordnungstabelle war für nginx zu lang – die Konfigurationsprüfung schlug fehl. Mit einer fehlerhaften Konfiguration startet der Webserver nicht. Solche Dinge findet man lieber im Test als an einem Abend, an dem die Website ohnehin schon wackelt.

6. Der Beifang: ein Crawler, der sich höflich vorstellt

Bei der Nachmessung der Browser-Versionen fiel eine Zahl aus dem Rahmen. Eine einzelne, eigentlich aktuelle Version kam auf mehrere Tausend Anfragen am Tag. Dahinter stand kein Schwarm, sondern ein KI-Crawler, der sich in seiner Kennung offen zu erkennen gibt: rund 6.400 Anfragen an diesem Tag, in der Woche davor täglich 7.000 bis 10.000, etwa vier Fünftel davon auf genau den teuren Seiten.

Er war durch beide Raster gefallen. In der Liste der bekannten Crawler stand er schlicht noch nicht. Die neue Regel für veraltete Browser traf ihn nicht, weil seine Version aktuell genug ist. Von seinen 6.400 Anfragen war keine einzige gedrosselt worden.

Er teilt sich jetzt das Kontingent mit den übrigen Crawlern. Wichtig war uns dabei die Zurückhaltung: Wir haben alle ähnlich gebauten Kennungen nachgemessen und genau diesen einen Eintrag ergänzt. Andere Kandidaten – darunter Suchmaschinen aus anderen Märkten – bleiben unangetastet, bis ihr Anteil an den teuren Seiten gemessen ist. Eine Liste, in die man auf Verdacht hineinschreibt, drosselt irgendwann die Falschen.

7. Die Kehrseite derselben Woche: Wenn das Limit die eigene Redaktion trifft

Wer Limits setzt, trifft früher oder später jemanden, den er nicht meinte. In derselben Woche fanden wir in den Protokollen 466 abgewiesene Anfragen an zwei Tagen – alle aus dem Büro der Redaktion.

Die Redaktion hatte mehrere Hundert Flaschenbilder auf einmal in die Dateiverwaltung des Redaktionssystems TYPO3 gezogen. Der Browser schickt dabei für jede Datei zwei Anfragen, und zwar gleichzeitig: In einer einzigen Sekunde gingen 247 Anfragen ein. Die Obergrenze für gleichzeitige Verbindungen pro Adresse lag bei 100. Dass alle Redakteurinnen und Redakteure im Büro nach außen dieselbe Adresse haben, verschärfte das zusätzlich. Die zweite Hälfte des Problems: Beim Öffnen eines Ordners mit frischen Bildern fordert die Dateiliste alle Vorschaubilder auf einmal an. Was über die Begrenzung hinausging, erschien als defektes Bild.

Für den Server sieht ein Massen-Upload nicht anders aus als ein Angriff. Der Unterschied liegt darin, wer ihn auslöst – und diese Unterscheidung muss die Regel selbst treffen. Unsere Entscheidungen:

  • Eine höhere Grenze statt einer Ausnahme. Uploads im Redaktionsbereich haben jetzt eine eigene, deutlich höhere Obergrenze. Ganz ohne Grenze wäre es bequemer gewesen, aber jeder Upload belegt dieselben Server-Prozesse, die auch die Website ausliefern. Die Grenze ist an der Arbeitsweise der Redaktion bemessen, nicht geschätzt.
  • Vorschaubilder warten, statt zu scheitern. Eine volle Seite der Dateiliste lädt sofort, alles Weitere wird in ruhigem Takt nachgeliefert. Im ungünstigsten Fall wartet ein Vorschaubild rund 16 Sekunden. Vorher blieb es einfach leer.
  • Die Anmeldung bleibt streng. Für die Anmeldeseite und den Rest des Redaktionsbereichs gelten die bisherigen Limits unverändert.

Auch das ist mit echten Anfragen geprüft: 300 gleichzeitige Upload-Anfragen von einer Adresse ergaben vorher 100 angenommene und 200 abgewiesene, jetzt 300 angenommene. Auf der Anmeldeseite ergibt dieselbe Probe vorher wie nachher 16 angenommene und 44 abgewiesene von 60. Bei der Prüfung fiel zudem eine ältere Ausnahmeregel auf, die großzügiger war als beabsichtigt. Sie ist bei der Gelegenheit enger gefasst worden.

Die Vorgeschichte gehört dazu: Dieselbe Grenze war schon einmal angehoben worden, damals nach einem Upload von 56 Dateien. Die Redaktion arbeitet inzwischen mit größeren Mengen, und eine Grenze, die einmal gepasst hat, passt nicht für immer.

Das Ergebnis

Die Absicherung gegen den Schwarm war noch am Abend des Ausfalls geschrieben, geprüft und freigegeben. Seitdem gilt auf der Plattform:

  • Anfragen mit deutlich veralteter Desktop-Kennung auf den teuren Seiten teilen sich ein gemeinsames Kontingent – unabhängig davon, über wie viele Adressen sie kommen.
  • Besucher mit aktuellem Browser und geprüfte Suchmaschinen sind nicht betroffen.
  • Der bis dahin ungebremste KI-Crawler läuft im selben Kontingent wie die übrigen Crawler.
  • Die Redaktion kann mehrere Hundert Bilder auf einmal hochladen, ohne an einem Limit zu scheitern, das nie für sie gedacht war.

Zur ehrlichen Einordnung gehören zwei offene Punkte. Erstens: Dass die Regel einen echten Schwarm im Betrieb abfängt, ist auf dem Prüfstand belegt, aber noch nicht in freier Wildbahn. Der Nachweis sind abgewiesene Anfragen im Protokoll statt einer ausgelasteten Datenbank – und den gibt es erst, wenn der Schwarm wiederkommt. Bis dahin behaupten wir keinen Erfolg. Dasselbe gilt für den nächsten großen Upload der Redaktion. Zweitens: Die Drosselung behandelt das Symptom. Die eigentliche Schwachstelle sind Seiten, die bei jedem Abruf teuer berechnet werden. Sie schneller oder zwischenspeicherbar zu machen, ist der nachhaltigere Schritt und ein eigenes Vorhaben.

Was Betreiber großer Content-Seiten daraus mitnehmen können

  1. Limits pro Adresse schützen nur vor Absendern, die eine Adresse haben. Gegen einen Schwarm aus Tausenden Einzelanfragen sind sie strukturell blind – ebenso jede automatische Sperre.
  2. Das Verhältnis von Anfragen zu Adressen messen. Liegt es auf den teuren Seiten nahe eins zu eins, sind das keine Besucher.
  3. Die teuren Seiten kennen. Ein Angreifer braucht keine große Menge, wenn er die Seiten trifft, die jedes Mal frisch berechnet werden. Welche das sind, sollte man vor ihm wissen.
  4. Merkmale kombinieren, statt einem zu vertrauen. Eine veraltete Browser-Kennung allein ist harmlos, der Abruf einer Detailseite auch. Zusammen ergeben sie ein Muster.
  5. Im Zweifel drosseln statt sperren. Eine Drosselung verzeiht den Irrtum. Eine Sperre nicht.
  6. Limits müssen wissen, wen sie treffen. Redaktion und Außenwelt brauchen unterschiedliche Regeln – und die Anmeldung braucht die strengste.
  7. Jede Regel mit einem Test absichern, der auch die alte Konfiguration durchfallen lässt. Und die Schwellen aufschreiben: mit Messwert, Datum und dem Befehl, mit dem man nachmisst.

Ihre Website wird ohne erkennbaren Grund langsam – oder Sie sind nicht sicher, ob Ihre Limits die Richtigen treffen?

Wir schauen uns Ihre Protokolle und Ihre Limits gerne gemeinsam mit Ihnen an.

Projekt besprechen
Christopher Zechendorf
Christopher Zechendorf Christopher Zechendorf leitet die ext.dev GmbH und bringt über 25 Jahre Erfahrung in Webentwicklung, CMS-Systemen und Infrastruktur mit.