Zum Inhalt springen

Odoo 19 Community: Web RBL – Sperrliste für Angriffsverkehr auf den eigenen Webseiten

24. September 2026 durch
Odoo 19 Community: Web RBL – Sperrliste für Angriffsverkehr auf den eigenen Webseiten

Dreizehn Prozent unseres Webverkehrs waren Einbruchsversuche

Am Anfang stand keine Idee, sondern eine Protokolldatei. Sie war 496 Megabyte groß, umfasste siebzehn Tage, und beim Durchsehen fiel auf, dass die überwiegende Mehrheit der Fehlermeldungen darin nichts mit unserem Betrieb zu tun hatte. Es waren fremde Rechner, die unsere fünf Webseiten nach bekannten Schwachstellen absuchten.

Ausgezählt ergab das ein Bild, das wir so nicht erwartet hatten:

Gesuchtes ZielAnfragen
.env in 24 Schreibweisen60.817
Verzeichniswechsel (../)15.186
PHP- und ASP-Sonden13.372
WordPress-Pfade4.795
.git-Verzeichnisse4.467
Summe98.411

98.411 von insgesamt 715.799 Anfragen – dreizehn Prozent des gesamten Webverkehrs, verteilt auf 755 verschiedene Absender. Eine einzelne Adresse schickte an einem Tag 9.413 Sonden.

Das Teure war nicht der Angriff, sondern die Antwort

Kein einziger dieser Versuche hatte Aussicht auf Erfolg: Odoo hat keine .env, kein wp-admin und kein phpMyAdmin. Trotzdem kosteten sie uns erheblich mehr als ein Achselzucken.

Denn Odoo behandelt eine Anfrage auf einen unbekannten Pfad wie jede andere: Es sucht eine passende Route, findet keine, ruft die Webseiten-Fehlerseite auf und rendert sie über die volle Vorlagenmaschinerie. Enthält der Pfad Punkt-Segmente wie /@fs/../../.env, stolpert das Rendern zusätzlich beim Zusammensetzen der og:url-Metaangabe – und die Fehlerseite der Fehlerseite scheitert ebenfalls.

Aus einer einzigen Sonde wurden so zwei vollständige Stapelprotokolle. Zusammengerechnet machten diese Fehler 96 Prozent unseres gesamten Protokollaufkommens aus. Echte Fehler – die, die man sehen will – gingen darin unter.

Web RBL: erkennen, verbuchen, veröffentlichen

Aus dieser Auswertung ist ein Odoo-19-Modul entstanden, das wir unter der AGPL-3 veröffentlicht haben. Es setzt an drei Stellen an:

  1. Erkennen und abweisen, bevor Odoo mit der Wegfindung beginnt. Eine Sonde kostet damit ein knappes 403 statt einer gerenderten Fehlerseite – ohne Vorlage, ohne Protokollzeile.
  2. Buch führen: Welche Adresse hat wann wonach gesucht, und an wie vielen verschiedenen Tagen.
  3. Die Liste veröffentlichen, damit eine Firewall oder ein Reverse Proxy den Angreifer abweisen kann, bevor er Odoo überhaupt erreicht.

Zwei Listen mit sehr unterschiedlichem Gewicht

Der Kern des Konzepts sind zwei getrennte Listen. Die Trennung ist keine Spielerei, sondern folgt daraus, dass die beiden Listen unterschiedlich schwer wiegen.

Die einfache Liste: 24 Stunden, dann wieder frei

Wer nach /.env oder /.git/config sucht, landet beim ersten Versuch für 24 Stunden auf der Liste. Ab dann erreicht er die Webseiten überhaupt nicht mehr – auch nicht die Startseite.

Das klingt streng, ist aber der eigentliche Gewinn. Rechnet man es gegen die gemessenen Daten durch, kämen von 87.823 Sonden mit erkennbarer Absenderadresse nur noch 819 durch – eine je Adresse und Tag. Die Adresse mit den 9.413 Sonden käme mit genau einer davon ans Ziel; die übrigen 9.412 prallen ab.

Nach 24 Stunden läuft die Sperre aus, und das ist Absicht. 43 Prozent der Angreifer – 328 von 754 – schicken in unseren Daten genau eine Sonde und kommen nie wieder. Ihre Adresse gehört morgen womöglich einem anderen Mieter desselben Rechenzentrums. Eine dauerhafte Sperre würde dann einen Unbeteiligten treffen, und zwar in einem Umfang, der leicht übersehen wird: Wenn die Liste in einer Firewall landet, blockiert sie nicht nur Web, sondern auch Mail.

Wer dagegen an drei verschiedenen Tagen wiederkommt, wird dauerhaft gesperrt. Bewusst an drei Tagen und nicht nach drei Treffern: Dreißig Sonden in zwei Sekunden sind ein Vorfall. Wer am Montag, am Mittwoch und am Freitag wiederkommt, sucht nicht mehr – der hat uns auf einer Liste.

Die Hochrisiko-Liste: bewiesen, nicht vermutet

Die zweite Liste entsteht auf einem anderen Weg. Optional – und in unserem eigenen Betrieb bewusst abgeschaltet – kann das Modul auf die ersten Sondierungsversuche einer Adresse eine erfundene Antwort schicken: eine .env, eine .git/config oder das Wurzelverzeichnis eines Commodore 500.

Ein Gerät, das nie gebaut wurde. Das ist kein Scherz am falschen Platz, sondern Teil der Überlegung: Der Inhalt ist erkennbar Unsinn, enthält keine plausiblen Zugangsdaten und hat damit keinen Handelswert. Niemand kann behaupten, wir hätten ihn in etwas hineingelockt, was wie ein echtes System aussah.

Der Zweck ist auch nicht Täuschung, sondern Beweis. In jeder Fälschung steckt ein Wert, den es nirgendwo sonst gibt:

APP_NAME=Commodore
ADMIN_PATH=/cp-8f3a2b9e
DB_CONNECTION=cbm
DB_HOST=8050.local

Wird der Pfad /cp-8f3a2b9e je abgerufen, dann hat jemand unsere Fälschung gelesen und danach gehandelt. Das ist kein Verdacht und keine Heuristik – der Wert kann aus keiner anderen Quelle stammen. Ein Fehlalarm ist ausgeschlossen.

Solche Adressen wandern ohne Frist auf die Hochrisiko-Liste. Und diese Liste hat eine Eigenschaft, die die einfache nicht hat: Kommt der Abruf von einer anderen Adresse als derjenigen, die den Köder erhalten hat, dann wurden die Daten weitergegeben. Wir erfahren also nicht nur, dass der Zweitverwerter böswillig ist, sondern auch, von wem er die Daten hat.

Die Sicherheitsaspekte im Einzelnen

Ein Modul, das Besucher aussperren kann, ist selbst ein Risiko. Die folgenden Punkte sind deshalb keine Zutaten, sondern der eigentliche Entwurf.

1. Die Proxy-Erkennung – die gefährlichste Stelle überhaupt

Steht ein Reverse Proxy vor Odoo, ist die Absenderadresse jeder Anfrage die Adresse des Proxy. Wer auf dieser Grundlage sperrt, sperrt den Proxy – und damit auf einen Schlag jeden Besucher aller Webseiten. Aus dem Schutzmodul würde ein Totalausfall.

Das Modul fragt deshalb nicht zuerst „welche Adresse nehme ich?", sondern „darf ich überhaupt sperren?". Im Zweifel lautet die Antwort nein:

  • proxy_mode = True in der Odoo-Konfiguration und eine X-Forwarded-Host-Kopfzeile: Odoo hat die Adresse korrigiert, sie ist belastbar.
  • Es kommen X-Forwarded-For-Kopfzeilen an, aber proxy_mode ist nicht gesetzt: Es wird nicht gesperrt, und im Protokoll steht, warum.
  • Mehr als ein Eintrag in X-Forwarded-For: Odoo wertet nur einen Proxy-Sprung aus, die ermittelte Adresse wäre die eines Zwischenproxys. Es wird nicht gesperrt.

2. Eigene Netze niemals

Private Adressbereiche nach RFC 1918, Loopback, Link-Local und Carrier-Grade-NAT stehen auf einer festen Ausnahmeliste, die sich nicht abschalten lässt. Eine Sperre dort träfe den Proxy, einen Kollegen im Haus oder den Server selbst – nie einen Angreifer aus dem Netz.

3. Ein eigener Fehler darf die Webseite nicht mitnehmen

Die gesamte Prüfung steckt in einer Ausnahmebehandlung. Was darin schiefgeht – ein Datenbankfehler, ein fehlerhaftes Muster, eine fehlende Tabelle – wird protokolliert, und die Anfrage läuft unverändert weiter. Ein Schutzmodul, das beim eigenen Fehler die Webseite mitnimmt, ist schlimmer als gar keines.

4. Beobachten vor Sperren

Nach der Installation ist das Sperren abgeschaltet. Sonden werden abgewiesen und verbucht, gewöhnliche Anfragen gelisteter Adressen laufen aber durch. So lässt sich einige Tage mitlesen, wer tatsächlich auf der Liste landet, bevor das Modul jemanden aussperrt.

Das hat sich bei uns bezahlt gemacht: Die ursprünglich geplante Schwelle „drei Tage Wiederholung" hätte über siebzehn Tage genau fünf Adressen dauerhaft gesperrt – weil Scanner aus Rechenzentren bei jedem Start eine neue Adresse bekommen. Die eigentliche Wirkung liegt bei der 24-Stunden-Sperre, nicht bei der Dauersperre.

5. Enge Muster statt breiter Netze

Die Erkennungsmuster sind bewusst schmal gehalten: Ein falsch erkannter Besucher ist teurer als eine übersehene Sonde. Jedes Muster trägt außerdem, ob es sperrt oder nur zählt, und das lässt sich je Muster umstellen.

Ein Beispiel für diese Vorsicht: Ein fremder Webmaster, der ein Bild relativ falsch verlinkt (/bilder/../logo.png), soll seine Besucher nicht bei uns aussperren. In der Praxis normalisiert der Webserver solche Pfade ohnehin, bevor das Modul sie sieht – die Unterscheidung greift beim doppelt kodierten Fall, den niemand versehentlich erzeugt.

6. Eine Freigabe von Hand bleibt bestehen

Wird eine Adresse in der Oberfläche freigegeben, bleibt sie frei – auch wenn sie erneut auffällt. Wer sie freigegeben hat, hatte einen Grund; ihn stillschweigend zu überstimmen wäre die schlechtere Überraschung. Die Treffer werden weiter mitgezählt und sind einsehbar, nur die Sperre bleibt aus.

7. Die Liste ist geschützt, aber kein Geheimnis

Der Endpunkt, der die Liste ausliefert, verlangt einen Token in der Adresse. Ohne gültigen Token antwortet er mit 404 statt mit 403: Wer ihn nicht hat, soll nicht einmal erfahren, dass es den Endpunkt gibt. Der Vergleich läuft in konstanter Zeit, damit sich der Token nicht über die Antwortdauer erraten lässt.

Der Schutz dient dabei weniger der Geheimhaltung – wer die Liste liest, erfährt nur, wer uns angegriffen hat – als der Sorgfalt: Eine unbedacht übernommene fremde Sperrliste trifft im eigenen Netz womöglich einen echten Besucher.

8. Der Köder verrät nichts Verwertbares

Die erfundenen Dateien enthalten keine plausiblen Zugangsdaten, keine existierenden Hostnamen und keine Schlüssel in gültiger Form. Das ist Absicht: Ein glaubwürdig gefälschter Schlüsselbund landet in Scanner-Datenbanken und bringt am Ende mehr Verkehr statt weniger.

Der Kanarienwert ist deshalb immer etwas, das man bei uns abruft – ein Pfad, ein Token in der Adresszeile. Ein erfundener Datenbankname sieht zwar echt aus, beweist aber nie etwas, weil wir nie erfahren, ob ihn jemand benutzt hat. Und weil ein 200 auf /.env für manche Scanner selbst schon ein Signal ist, wird der Köder abgeschaltet ausgeliefert. Wer ihn einschaltet, soll das bewusst tun.

9. Eine Obergrenze für die Köder

Nach fünf erfundenen Antworten je Adresse ist Schluss, danach gibt es wieder 403. Wer bis dahin nicht angebissen hat, beißt nicht mehr an – und eine Adresse mit 9.413 Sonden soll uns nicht 9.413 erfundene Dateien kosten.

Einbindung in Firewall und Reverse Proxy

Die Liste wird im ärmsten denkbaren Format ausgeliefert: eine Adresse je Zeile, sonst nichts. Kein JSON, keine Kopfzeile. Genau das lesen HAProxy, nftables und ipset ohne Umweg.

curl -s -o /etc/haproxy/rbl.lst \
  "https://www.example.com/web_rbl/liste?token=..."

frontend web
    acl gesperrt src -f /etc/haproxy/rbl.lst
    http-request silent-drop if gesperrt

Damit erreicht ein gelisteter Angreifer Odoo gar nicht mehr. Eine zweite Adresse liefert nur die Hochrisiko-Einträge – die kann man ohne schlechtes Gewissen dauerhaft in eine Firewall hängen, weil dort nur steht, wer nachweislich gehandelt hat.

Zusätzlich gibt es eine JSON-Fassung mit Zustand, Trefferzahl und Frist für eigene Auswertungen, sowie eine Verwaltung im Odoo-Backend: Sperrliste, einzelne Treffer nach Muster gruppiert, und je Eintrag die Möglichkeit, freizugeben oder dauerhaft zu sperren.

Was das Modul nicht kann

Es ersetzt keine Firewall und keine Web Application Firewall. Es erkennt bekannte Sondierungsmuster in Pfaden – nicht Angriffe auf Formulare, nicht Einschleusungsversuche in Parameter, nicht Anmeldeversuche mit geratenen Kennwörtern.

Es sieht außerdem nur, was über HTTP zu uns zurückkommt. Was ein Angreifer mit erbeuteten Datenbank- oder Postfachzugängen anstellt, bleibt unsichtbar – und genau deshalb sind die Kanarienwerte Pfade und keine Zugangsdaten.

Verfügbarkeit

Das Modul steht unter der AGPL-3 öffentlich zur Verfügung:

github.com/Alex0176/odoo-web-rbl

Es läuft bei uns produktiv auf fünf Webseiten. Den Köder haben wir dabei abgeschaltet gelassen – die Sperre allein nimmt uns den Großteil des Lärms ab, und die erfundenen Antworten wollten wir uns für den Fall aufheben, dass wir genauer wissen müssen, wer da eigentlich sucht.