Urls und andere Zeichenketten in HTTP-Anfragen mit mod_substitute ersetzen (Apache2)

Urls und andere Zeichenketten in HTTP-Anfragen mit mod_substitute ersetzen (Apache2)

Das Apache-Modul mod_substitute erlaubt die Ersetzung von Zeichenketten in HTT-Anfragen. Und zwar nicht nur im Header, sondern auch dem Body. Dank regulärer Ausdrücke ist es sehr mächtig.

Warum Zeichenketten im HTTP-Body ersetzen?

Im besten Falle benötigt man dieses Modul nicht, weil die Webanwendung selbst einwandfrei funktioniert. Oder man diese wie gewünscht anpassen kann. Allerdings gibt es im Alltag verschiedene Konstellationen, in denen das nicht ohne weiteres möglich ist.

Beispiele:

  • URLs/Weiterleitungen sind nicht korrekt. Dies kann/darf in der Anwendung nicht behoben werden
  • Eine Applikation soll über mehrere URLs erreichbar sein, unterstützt dies aber nicht offiziell
  • Proprietäre Software, deren Quellcode nicht vorliegt, soll in kleinerem Maße angepasst oder erweitert werden

Solche Ersetzungslösungen sollten nicht pauschal angewendet werden! Im Regelfall ist es in verschiedenster Hinsicht besser, diese wenn möglich direkt im Backend durchzuführen. Bei quelloffener Software z.B. kann die Software wie gewünscht angepasst werden. Nur wenn es keine bessere Alternative gibt, sollte man diesen Weg gehen.

Was ist zu beachten?

Gzip ist in Kombination mit mod_substitute problematisch: Die Inhalte werden in diesem Falle nicht dekompromiert, um diese zu Ersetzen und anschließend wieder zu kompromieren. Dies wäre performancetechnisch auch nicht unbedingt zu empfehlen. Grundsätzlich muss man mit mod_substitute jedoch nicht per se vollständig auf die Gzip-Kompression verzichten.

Apache nutzt Gzip für eigene Inhalte

Soll der Apache-Webserver lokal ausgelieferte Inhalte kompromieren, ist schlicht die Reihenfolge entscheidend: mod_substitute muss vor der Deflate-Kompression stattfinden. Dann wird erst Ersetzt und anschließend Komprimiert. In umgekehrter Reihenfolge findet keine Ersetzung statt! Mit der korrekten Reihenfolge kann Gzip also vollständig genutzt werden.

Proxy-Server hinter Apache nutzt Gzip

Fungiert Apache mit mod_proxy als Reverse-Proxy für einen anderen Webserver, dürfen die zu ersetzenden Mime-Typen keine Gzip-Kompression aufweisen. Ansonsten wird auch hier nichts Ersetzt. Je nachdem um welches Backend es sich handelt, kann man ggf. nur bestimmte Typen komprimieren lassen.

Beispiel Bitbucket:

Hier war gewünscht, den Bitbucket-Server sowohl intern als auch extern erreichbar zu machen. Allerdings mit verschiedenen Adressen. In der Konfigurationsdatei ${BB_DATA}/shared/bitbucket.properties lassen sich die Mime-Types festlegen, für die eine Kompression angewendet werden soll. Möchten wir Ersetzungen nur in HTML-Seiten durchführen, entfernen wir einfach text/html aus dieser Liste:

server.compression.mime-types=text/css,text/javascript,text/json,text/plain,text/xml,text/x-javascript

Alternativ kann die Kompression mit folgendem Schalter auch vollständig abgeschaltet werden:

server.compression.enabled=false

Alternativ: Gzip für das Backend via Apache deaktivieren

Falls es keine Möglichkeit gibt die Komprimierung im Backend zu deaktivieren, gibt es einen Trick: Der Browser sendet die von ihm unterstützten Verfahren im Accept-Encoding Header an den Server. Der Hintergrund ist, dass es verschiedene Kompressionsverfahren (z.B. Gzip oder Brotli) gibt. Nicht alle Clients unterstützen sämtliche Verfahren. Teilt der Client dem Server über diesen Header nicht mit, dass er beispielsweise Gzip unterstützt, findet keine Kompression statt.

Dies können wir uns zunutze machen, um das Backend zu zwingen, uns die Daten im Klartext auszuliefern:

RequestHeader unset Accept-Encoding

Je nach Applikation kann dies auch nur für bestimmte Lokationen gesetzt werden.

Ersetzungen von Zeichenketten mit mod_substitute

Voraussetzung ist zunächst, dass mod_substitute und mod_filter geladen sind. Das Filter-Modul wird benötigt, um die Ersetzungen auf bestimmte Mime-Types beschränken zu können. Vor allem wenn nur einzelne Typen (z.B. HTML) betroffen sind, kommt dies der Performance zugute: Der Apache-Webserver muss die Regex-Ersetzungen dann nicht für jede Anfrage durchführen.

LoadModule filter_module /usr/lib64/apache2-prefork/mod_filter.so
LoadModule substitute_module /usr/lib64/apache2-prefork/mod_substitute.so

Je nach Distribution und Installationsart finden sich die Module in einem anderen Verzeichnis. Falls man dies nicht kennt, einfach nach LoadModule in der httpd.conf suchen. Möglicherweise sind die obigen Zeilen dort auch bereits vorhanden und müssen nur auskommentiert werden.

Sind beide Module geladen, kann man mit der Substitute Direktive überall Ersetzungen durchführen.

AddOutputFilterByType SUBSTITUTE text/html
Substitute "s|server1|server2|i"

AddOutputFilterByType aktiviert Ersetzungen für die angegebenen Mime-Typen. In diesem Falle wird nur ersetzt, wenn dieser text/html lautet. Es können auch mehrere mit Leerzeichen getrennt angegeben werden.

Substitute erwartet ein Pattern mit optionalen Flags, wie man es von Unix/Linux-Tools wie grep oder sed bereits kennt. Obiges Beispiel ersetzt alle Vorkommen von server1 mit server2 und ignoriert dabei die Groß/Kleinschreibung. Server1 wird dadurch eben so ersetzt wie SeRvEr1. Der Grundsätzliche Aufbau des Patterns ist

"s|SUCHBEGRIFF|ERSETZUNG|FLAGS"

Die Pipe | ist in diesem Falle der Delimiter. Er trennt diese drei Segmente voneinander und ist frei wählbar. Oft wird in der Linux-Welt ein Schrägstrich genutzt. Da URLs oft selbst einen enthalten, empfehle ich hier ein anderes Trennzeichen zu nutzen. Neben der Pipe eignet sich auch die Raute.

Die wichtigsten Flags:

  • i (Ignose cases): Ignoriert die Groß/Kleinschreibung
  • n (No Regexp): Ersetzt nur String-Zeichenketten. Fehlt dieses Flag, wird der Suchbegriff als regulärer Ausdruck betrachtet. Somit wäre z.B. das Pattern example.(de|com) gültig, um sowohl example.de als auch .com zu ersetzen

Mehrere Flags lassen sich ohne Trennzeichen kombinieren, beispielsweise in um Strings unabhängig von ihrer Schreibweise zu ersetzen.

Leave a Reply