1 Punkte von GN⁺ 2024-03-31 | 1 Kommentare | Auf WhatsApp teilen
  • TablePlus beobachtet trotz seit Wochen anhaltender DDoS-Versuche den Serverzustand, ohne IPs zu blockieren oder den Cloudflare-Modus „Under Attack“ zu aktivieren
  • In den letzten 30 Tagen gab es rund 6 Millionen Versuche, die Installationsdatei herunterzuladen; allein in den letzten 5 Tagen waren es 800.126, bei einer Dateigröße von etwa 200 MB pro Download
  • Während des Angriffs lag die CPU-Auslastung des Servers meist bei 0–1 %; rund acht API-Services und Datenbanken sind so konfiguriert, dass sie ohne Cache monatlich Milliarden von Requests verarbeiten können
  • Das Backend ist als monolithischer Service je App gebündelt; statt Docker, Kubernetes oder Runtime-Umgebung wird ein einzelnes Binary auf einen neuen VPS deployt
  • Einfache Bausteine wie Go/Rust-Frameworks, Datenbank-Indizierung, getrennte Log-Datenbank, Nginx-Reverse-Proxy, Cloudflare CDN/R2 und Throttling beim E-Mail-Versand senken Angriffskosten und operative Komplexität

Wochenlange DDoS-Versuche

  • Jemand sendet seit Wochen Hunderte Millionen Requests an die TablePlus-Server und versucht, Installationsdateien millionenfach herunterzuladen
  • In den letzten 5 Tagen gab es 800.126 Download-Versuche für die Installationsdatei, in den letzten 30 Tagen rund 6 Millionen
    • Die Installationsdatei ist pro Download etwa 200 MB groß
  • Gemessen an den API-Aufrufen der letzten 30 Tage stammt der Großteil des Traffics aus der EU, insbesondere aus Germany und dem United Kingdom
    • Diese Zahlen enthalten keine Download-Requests und keinen CDN-Traffic
  • Zum Zeitpunkt des Schreibens läuft der Angriff weiterhin

Tatsächliche Reaktion: eine Architektur, die standhält, statt zu blockieren

  • Angreifer-IP-Adressen werden nicht blockiert
  • Cloudflare wird genutzt, aber der „Under Attack“-Modus ist nicht aktiviert
  • Selbst während des Angriffs bleibt die Server-CPU meist bei 0–1 % und ist praktisch im Leerlauf
  • Da der Service monatlich Milliarden von Requests problemlos verarbeiten kann und die Kostenbelastung nicht groß ist, werden kaum gesonderte Maßnahmen ergriffen

Einfaches Backend-Design

  • Das Design der TablePlus-App setzt auf Einfachheit, und auch die Backend-Services werden möglichst in einer Minimal-Konfiguration gehalten
  • Drittanbieter-Rendering-Services wie Vercel oder Netlify werden nicht genutzt, weil sie während eines Angriffs zu Engpässen oder unerwarteten Rechnungen führen können
  • Mit einem eigenen Webserver lassen sich solche Einschränkungen aus ihrer Sicht vermeiden
  • Früher konnte ein Monolith wegen schwacher VPS und Prozessoren zum Flaschenhals werden, heutige VPS bieten jedoch Multicore-CPUs, viel RAM und schnelle SSDs
    • Schnelle SSDs und RAM verbessern die Datenbank-Performance erheblich
    • Richtig implementiert kann auch ein monolithischer Service auf einer einzelnen Instanz monatlich Milliarden von Requests verarbeiten

Betriebsmodell: ein Monolith pro App

  • APIs, Website, E-Mail, Zahlungen und weitere für eine App benötigte Funktionen werden in einem Service zusammengeführt
  • Das Deployment besteht nur aus einer Konfigurationsdatei, Build und Deployment
  • Beim Umzug zu einem anderen Cloud-Anbieter oder bei einem neuen Deployment lässt sich der Service schnell bereitstellen
  • Wenige Abhängigkeiten erleichtern Debugging und die Identifikation von Flaschenhälsen
    • Tritt ein Fehler auf, gibt es nur einen oder wenige Services zu prüfen
  • Beispiele für mögliche Monolith-Frameworks sind:
    • Golang: Echo, Gin
    • PHP: Laravel
    • Ruby: Rails
    • Rust: Actix, Rocket, Warp

Konfigurationsprinzipien für monatlich Milliarden Requests

  • Ein performantes Web-Framework wird gewählt; TablePlus bevorzugt Golang und Rust
  • Wenn Datensätze wachsen, werden Indizes in der Datenbank gesetzt, um Abfragezeiten zu verkürzen
  • Zum Schutz der Kern-Business-Performance werden die Hauptdatenbank und die Datenbanken für Logs und Nutzungsdaten getrennt
    • Die Hauptdatenbank ist für Daten gedacht, die sich nicht ändern oder deren Umfang über die Zeit nicht wächst
    • Die Log- und Nutzungsdatenbank ist für Daten gedacht, die mit der Zeit kontinuierlich wachsen
  • Vor den Kern-APIs sitzt ein Reverse Proxy, der Requests verarbeitet und verteilt
    • Das hilft, wenn mehrere Server benötigt werden
    • TablePlus nutzt Nginx
  • Alles läuft hinter Cloudflare, mit passend konfiguriertem Cache, Argo und full SSL zwischen Cloudflare und Server
  • Es wird ein CDN mit DDoS-Schutz genutzt
    • TablePlus bevorzugt zur Kostensenkung und zum Schutz Cloudflare R2 und CDN gegenüber Amazon CloudFront + S3
  • Werden große Download-Dateien ohne CDN oder Caching auf einem VPS abgelegt, ist die Bandbreite schnell aufgebraucht
    • TablePlus nutzt das Cloudflare CDN mit unbegrenzter Bandbreite
    • Wenn Argo auf derselben Domain aktiviert ist, kann Cloudflare-CDN-Traffic über Argo laufen; Argo-Bandbreite ist nicht kostenlos, wodurch die Kosten stark steigen können
  • Requests, bei denen der Server E-Mails senden muss, etwa bei „Passwort vergessen“, schützen den Mailer per Throttling

Deployment: Binaries ausführen, ohne Container

  • TablePlus hält den Deployment-Prozess so einfach wie möglich: ohne Docker, Kubernetes, Container oder separate Runtime-Konfiguration
  • Die Deployment-Einheit ist ein Binary
    • Es wird auf einen Linux-Server kopiert und als Prozess ausgeführt
    • Es wird ähnlich behandelt wie das Starten der TablePlus-App unter macOS
  • Linux Systemctl kann den Prozess überwachen und ihn bei einem fatalen Fehler neu starten
  • Die Ausführung erfolgt nativ, um keine CPU/RAM-Ressourcen an Zwischenschichten wie VMs, Virtualisierung oder Drittanbieter-Infrastruktur-Manager zu verschwenden
  • Go und Rust werden gewählt, weil sie performante Sprachen sind und Binary-Dateien für das Deployment erzeugen können

Schutzfunktionen, die bei Vercel aktiviert sein sollten

  • Vercel bietet für solche Situationen Spend Management und Attack Challenge Mode als Schutzfunktionen für Websites an
    • Spend Management: ermöglicht das Setzen eines weichen oder harten Ausgabenlimits
    • Attack Challenge Mode: ähnelt Cloudflares „Under Attack“-Modus
  • Wer Vercel nutzt, sollte diese Funktionen aktivieren

1 Kommentare

 
GN⁺ 2024-03-31
Meinungen auf Hacker News
  • Dieser Beitrag wirkt extrem nach Selbstbeweihräucherung. „1 Milliarde Requests pro Monat“ sind nur ein paar Hundert Requests pro Sekunde und damit eher trivial; das als DDoS zu bezeichnen, fällt schwer.
    Außerdem liegt die Site hinter Cloudflare als CDN, daher verstehe ich noch weniger, warum man das aus Performance-Sicht als besondere Leistung sehen sollte.
    Es gibt zum Beispiel kein vernünftiges Szenario, in dem eine 200-MB-Datei nicht aus dem CDN-Cache ausgeliefert wird. Darauf stolz zu sein, dass der Applikationsserver bei jedem Download nicht 200 MB von der Platte liest und zum Client kopiert, ist fehl am Platz; das wäre schlicht offensichtlich schlechtes Design.

    • Falls die erwähnte 200-MB-Datei der Download der TablePlus-Client-App unter https://tableplus.com/download ist: Die Windows-Version ist 183 MB groß und wird tatsächlich von Cloudflare gecacht.

      # curl -v https://files.tableplus.com/windows/5.9.2/TablePlusSetup.exec > /dev/null

      < cache-control: max-age=691200

      < cf-cache-status: HIT

      < age: 2980

    • Ist das nicht normalerweise etwas, wofür man bei einer FAANG-Firma meinen würde, man brauche über 1.000 Entwickler? Ich weiß nicht, wie man etwas, das selbst große Unternehmen regelmäßig nicht hinbekommen, einfach nur als Selbstbeweihräucherung abtun kann.

    • Es ist schwer, das nur als ein paar Hundert Requests pro Sekunde zu betrachten. Die Request-Dichte ist über die Zeit nicht gleichmäßig und kann häufig auf das 20-Fache des Durchschnitts steigen.

  • 4 TB pro Monat sind doch eigentlich kaum als DDoS-Angriff zu bezeichnen, oder? 4 TB pro Stunde könnte man DDoS nennen, aber 4 TB pro Monat sind nur 1,5 MB pro Sekunde.
    6 Millionen Requests pro Monat sind ebenfalls nur 2 Requests pro Sekunde. Bei dieser Größenordnung wirkt es nicht besonders relevant, dass der Dienst als Monolith betrieben wird – erst recht, wenn Cloudflare die meisten Requests über den CDN-Cache bedient.

    • Der allergrößte Teil des Webs besteht aus WordPress-Sites auf Multi-Tenant-Hosting für 5 bis 20 Dollar im Monat; bei größeren Organisationen ist es oft Drupal. Ohne Cache, direkt an die Datenbank angebunden, können die meisten Websites im Internet schon bei 4 Requests pro Sekunde ausfallen.

      Das klingt verrückt, ist aber Realität. Ich habe früher bei Cloudflare DDoS-Schutz, WAF, Firewalls und Sicherheitsprojekte für Kunden betreut und oft gesehen, dass Kundenseiten allein durch Web-Scraping oder sehr kleine, niedrige HTTP-Request-Raten ausfielen.

      Große Zahlen bestimmen die Schlagzeilen, aber was die meisten Menschen tatsächlich spüren, sind kleine Zahlen.

    • Ein Denial-of-Service-Angriff ist auch mit ein paar KB pro Stunde möglich. Wenn man einen schweren API-Endpunkt des Zieldienstes trifft, kann das allein den Dienst zu Fall bringen; „Distributed“ bedeutet nur, dass mehrere Maschinen gleichzeitig angreifen.

      Für DDoS ist nicht zwingend riesiger Durchsatz nötig. In den Nachrichten landen allerdings meistens die großen Angriffe.

      Ziel solcher Angriffe kann sein, das Bandbreitenkontingent des Ursprungsservers aufzubrauchen oder Bandbreitenkosten zu verursachen, die nicht mehr tragbar sind. Das geht sehr billig mit einer einzelnen oder wenigen Angriffsmaschinen. Die meisten Rechenzentren und Hoster haben Bandbreitenlimits oder berechnen nach einer bestimmten Menge zusätzlich; viel zu oft merken angegriffene Unternehmen es erst, wenn sie eine unbezahlbare Rechnung bekommen.

    • Ich habe eine Website für Game-Downloads mit insgesamt vielleicht 50 Spielern. Niemand nutzt sie, und echte Menschen laden das Spiel vermutlich nur ein paar Mal pro Monat herunter.
      Trotzdem hat eine einzelne 2-GB-Zip-Datei letzten Monat 5 TB Traffic verursacht, und das läuft seit Jahren so. Das ist kein DDoS, sondern nur ein falsch konfigurierter Bot/Crawler, der allen Links folgt und Downloads nicht abbricht.

    • Es scheint eher etwa 1 TB pro Tag zu sein, aber selbst das ist immer noch sehr wenig.

    • Hängt wohl davon ab, wie unregelmäßig der Traffic ist.

  • Das ist nur eine statische Marketing-Website für eine Desktop-App. Es gibt kein Diskussionsforum, Feedback läuft über GitHub-Issues.
    Es ist ziemlich seltsam, damit anzugeben, wie simpel das Deployment einer statischen Marketing-Site ist und dass statische Dateien auch millionenfach pro Tag heruntergeladen werden können. Und die Mitigation übernimmt hier Cloudflare, nicht sie selbst – falls bei so kleinem Traffic überhaupt Mitigation nötig ist.

    Wenn ich so einen „Angriff“ abbekommen hätte, hätte ich ihn vermutlich nicht einmal bemerkt, bis Cloudflare mir die monatliche E-Mail „X TB übertragen, nahezu 100 % Bandbreite eingespart“ schickt.

    Die App selbst mag ich und kann sie empfehlen.

    • Ich mag die App auch, aber dieser Beitrag klingt, als hätte man ChatGPT gebeten, einen ungewöhnlichen Marketingtext zu schreiben. Insgesamt ergibt vieles keinen Sinn, erst recht nicht für jemanden, der echte DDoS-Angriffe erlebt hat.
    • Der Punkt „damit anzugeben, wie simpel das Deployment ist“ dürfte meiner Meinung nach eher häufiger vorkommen. Je mehr Leute erkennen, dass ihre übermäßig komplexe Infrastruktur nicht optimal ist, desto besser.
  • Das wirkt weniger wie ein DDoS-Angriff, sondern der Beschreibung nach eher wie etwa 8 TB zusätzlicher Traffic pro Monat durch „nervig sinnlosen Missbrauch eines Dienstes“.
    Solchen Missbrauch kann man ignorieren, solange er keine Kosten- oder Ressourcenerschöpfungsprobleme verursacht. Aber Geschichten wie „Wie sich herausstellte, erledigten 80 % der Kapazität der Auto-Scaling-Flotte keinerlei nützliche Arbeit“ sind deprimierend häufig, also sollte man es zumindest im Blick behalten.

    Der nervigste Teil bei solchem Missbrauch sind meist die Logs. Der Großteil der Logs füllt sich damit, dass dieselben Hosts immer wieder denselben sinnlosen Kram tun. Wenn Log-Speicher billig und reichlich vorhanden ist, ist das kein großes Problem; aber eine Methode, bestimmten Traffic automatisch als Missbrauch zu klassifizieren und die normale Verarbeitung zu unterdrücken, ist definitiv eine gute Idee.

    Allerdings ist das leichter gesagt als getan. Ich kann nicht zählen, wie oft ich Klassifizierungslogik ergänzt habe, um offensichtlichen und dummen Missbrauch auf eingehenden SMTP-Servern zu erkennen – und jedes Mal wurden auch grenzwertig gültige Szenarien miterfasst.

    Wenn man zu viel Zeit in endlosen Rabbit Holes verbringt, kommt man leicht nicht mehr zur eigentlichen Arbeit. Deshalb kann es manchmal besser sein, das auszulagern – oder, wenn auch das zu umständlich oder teuer ist, den Missbrauch trotz des Ärgers einfach zu ignorieren.

    • Bei AWS kosten 8 TB ausgehender Traffic trotz 1 TB Freikontingent pro Monat 595 Dollar; bei Hetzner beginnt es bei unter 10 Dollar.
      Bei DigitalOcean zahlt man zum Beispiel beim s-4vcpu-8gb-intel für 3 TB über den enthaltenen 5 TB zusätzlich 30 Dollar, bei Linode kosten 3 TB Mehrverbrauch 15 Dollar. Insofern hat der Beitrag schon einen Punkt.
    • Wenn man es dauerhaft ignoriert, ermutigt man nur zu noch verdächtigerem Verhalten. Genau diese Haltung hat das Internet heute fast zu einem Sumpfgebiet gemacht.
  • Das ist kein bemerkenswerter „Angriff“. Das ginge schon mit einem einzelnen bash-Skript, das auf irgendeiner Maschine außer Kontrolle geraten ist.
    „50 Millionen Requests pro Monat aus Großbritannien“ sind im Schnitt nicht einmal 20 Requests pro Sekunde. Ich würde erwarten, dass ein einzelner Go-Server ohne große Optimierung das 250-Fache davon verarbeiten kann.

    Der Rat an sich ist nicht unbedingt schlecht, aber diese Zahlen sind kein Beleg dafür. Bei einem Go-HTTP-API-Service würde ich erwarten, dass er auf einem kleinen bis mittleren VPS auch mit etwas Datenbankarbeit und JSON-Formatierung ohne Optimierung 5.000 Requests pro Sekunde schafft. Diese Zahl basiert auf Erfahrung mit Dutzenden ähnlichen Services, die ich in Umgebungen deployed habe, die mehrere Milliarden Requests pro Tag erhielten und zu Spitzenzeiten über 500.000 Requests pro Sekunde lagen.

    • Wenn solcher Traffic als wiederholte Requests auf eine API prallt und alles von unauffälligen Standorten kommt, sollte man zuerst an eine versehentlich eingebaute Endlos-Retry-Schleife im Client-Code denken, nicht an DDoS.
  • Gut, dass das keinen Schaden anrichtet, aber als Ratschlag ist mir das etwas unheimlich, weil so viel fehlt. Es ist in Ordnung, Binär-Deployments gegenüber Docker zu bevorzugen, aber was ist mit dem Host, auf dem diese Binärdatei läuft? Einer der Gründe für Container ist, Security-Hardening-Einstellungen zusammen mit dem Deployment zu bündeln, damit man später beim Skalieren sicher sein kann, dass die Sicherheitseinstellungen über Nodes hinweg identisch sind.

    Der hier beschriebene Monolith kann auf einem einzelnen VPS laufen, und das ist gut und günstig. Aber wenn er crasht oder die Hardware aus irgendeinem Grund ausfällt, kann das zu ziemlich langer Downtime führen.

    Eine weitere Sorge ist, dass man die Defense in Depth des Application-Stacks verliert, wenn man alles in einen Monolithen packt. Wenn jemand die App über das Frontend kompromittiert, kann er direkt bis zum Backend-Datenspeicher durchgreifen. Deshalb legen viele Leute den Datenspeicher hinter interne Webservices und schirmen ihn mit Security Groups in einem privaten Netzwerk ab, getrennt vom Frontend, um die Angriffsfläche auf die Aktionen zu beschränken, die über den Browser ausführbar sind.

    • Es gibt keine Welt, in der man durch Vergrößern der Angriffsfläche bessere Sicherheit bekommt.

    • Wenn man glaubt, dass ein Host durch die Installation von Docker abgesichert ist, liegt man falsch. Wie konfiguriert man beim Skalieren zusätzliche Hosts?

      Wenn man Docker nutzt, müssen nicht nur die Container sicher sein, sondern auch der Host, also der Service, der die Container ausführt. Und wenn man skaliert und weitere Hosts ausrollt, müssen sie genauso sicher sein.

      Wenn man Infrastructure as Code und Configuration as Code nutzt, macht es im Kern keinen Unterschied, ob man nach der Systemkonfiguration eine Binärdatei oder Docker deployt.

    • Es gibt Tools, die „Bare-Metal“-Setups bis zu einem gewissen Grad reproduzierbar machen. Zum Beispiel NixOS, Ansible oder das Bauen von Amazon-AMI-Images.

    • Ich habe nie ganz verstanden, was mit „die App über das Frontend kompromittieren“ gemeint ist. SQL-Injection greift den Datenspeicher direkt an, ohne Remote Code Execution, und XSS wirkt sich horizontal auf andere Nutzer aus.
      Aber wie soll man vom Frontend aus frei bis ins gesamte Backend gelangen? Lassen Leute in ihren Go-API-Services JavaScript-Interpreter mit Shell-Zugriff laufen und rufen auf Nutzereingaben eval auf? Das wirkt technisch sehr konstruiert.

    • Ist das am Ende nicht einfach Security through Obscurity? Nach dem Motto: Wenn wir es so komplex machen, dass wir es selbst nicht verstehen, versteht es auch sonst niemand?

      Entscheidend ist nicht, mehr Mauern zu bauen, sondern die Mauern unzerstörbar zu machen. Interfaces senken die Performance und erhöhen die Komplexität.

  • Eine Formulierung, die mich persönlich stört. „1 Milliarde Requests pro Monat“ sind ungefähr 370 Requests pro Sekunde. Das ist ein Niveau, das ein gut konfigurierter einzelner Server schaffen kann, und es braucht sicher weniger als 10 Server.
    Schon ein einzelnes bösartiges bash-Skript kann so viel Traffic erzeugen.

    • Dank Microservices brauchen wir 45 Kubernetes-Nodes, um 1.000 Requests pro Sekunde zu verarbeiten.
    • Wenn diese Requests zeitlich gleichmäßig verteilt sind, ja. In einer Angriffssituation können Requests aber plötzlich in die Höhe schießen und sich danach abflachen, und trotzdem kommt man über 30 Tage auf 1 Milliarde Requests pro Monat.
    • Nicht nur mit einem gut konfigurierten Server: Sobald der JVM-JIT gearbeitet hat, geht das auch mit einem einzigen Core.
  • Vielleicht fehlt mir das Gefühl für die Realität, aber mehrere Milliarden Requests pro Monat klingen nicht nach viel. Gilt das als großer DDoS-Angriff?

    • Hängt davon ab, wer die Kosten trägt. Bei Self-Hosting ist das kein Problem, aber bei Serverless ist selbst dieses Niveau normalerweise teuer, besonders wenn der Traffic wertlos ist.
    • Für eine vernünftig entworfene API sind 300–400 API-Requests pro Sekunde keine schwere Last. 300–400 Requests pro Sekunde für statische Dateien sind weniger als Kleinkram.
    • Stimmt. 1 Milliarde Requests pro Monat sind im Schnitt 380 Requests pro Sekunde, also nicht so hoch.
    • Hängt von vielen Faktoren ab. Normalerweise provisioniert man Infrastruktur für die erwartete Nutzung, und übermäßige Reserve ist Verschwendung.
  • Mir gefiel der Teil: „Für jede App bauen wir einen Monolith-Service, der einfach zu deployen und zu warten ist. Kein Docker, kein Kubernetes, keine Abhängigkeiten, keine Runtime-Umgebung, nur eine einzige Binärdatei, die auf jedem neu erstellten VPS deployed werden kann.“

    • Ich nutze TablePlus nicht selbst. Wenn es hier um die Marketing-Website geht, ist klar, dass Kubernetes nicht nötig ist.
      Wenn es um die Anwendung geht, wundert mich die Aussage, es gebe keine Abhängigkeiten. Speichert sie keine Daten und schreibt keine Logs?

    • Das ist ein wirklich großartiger Vorteil von Golang. Man startet einen VPS, setzt vernünftige Defaults, cross-kompiliert und führt die Binärdatei aus.

      Verglichen mit Deployments in Python, Node oder PHP gibt es deutlich weniger unnötige Komplexität.

      Schön wäre, wenn das Betreiben und Am-Laufen-Halten eines Datenbankservers auch so einfach sein könnte.

  • Beim Titel hatte ich etwas erwartet, das eher zu swole doge passt. Einem großen Teil der Ratschläge stimme ich zu, aber hinter Cloudflare zu stehen ist keineswegs nichts.
    Je nach Traffic-Verteilung hätte ein VPS auch ohne Cloudflare gut durchgehalten, und der Umfang wirkt auch nicht besonders groß. Interessant wären detailliertere Statistiken gewesen, etwa Requests pro Sekunde und wie viel Cloudflare abgefangen hat, bevor es den Origin erreichte.

    Ein russischer Layer-7-DDoS gegen schwedische Unternehmen, den ich kenne, war so groß, dass große Provider an Kapazitätsgrenzen stießen und ausfielen. Dazu gehörten Verizon, Azure Frontdoor, Cloudflare und auch der GCP Load Balancer. Gegen eine solche Größenordnung würde diese Strategie keinesfalls funktionieren.