Wie man Drupal dazu bringt, über Stratos SSL Reverse Proxy zu arbeiten

Gestern hatte ich zwei Stunden in einem Café, einen Cappuccino auf meinem Tisch, ein Stück Zitronenkuchen und kostenloses WLAN. „Cool“, dachte ich, „Ich werde einen Eintrag für meinen Blog schreiben!“. Wie romantisch. Gleich nachdem ich das Passwort meines Blog-Benutzers eingegeben hatte, dämmerte es mir: Die Verbindung zu meinem Blog war nicht verschlüsselt!

Jeder, der im lokalen WLAN schnüffeln konnte, hätte mein Passwort beim Eingeben abfangen und meine Blog-Benutzer-ID stehlen können! Hier erfahren Sie wie sie in strato auth code anfordern und richtig nutzen. Es hat einige Zeit gedauert (etwas mehr als die 2 Stunden, die ich hatte….), um dies herauszufinden, also hier ist ein Howto, wie man seine Login/Admin-Tasks sicher für eine Drupal-Instanz macht, die auf Strato als Hoster läuft.

Die Grundidee

Die Grundidee hinter der Sicherung Ihrer Drupal-Administrator-Logins und -Aktionen ist es, sicherzustellen, dass der gesamte Anmelde- und Admin-Verkehr über SSL läuft. Auf diese Weise sind Passwörter, Authentisierungs-Cookies usw. immer verschlüsselt und schwer zu erfassen.

Dies klingt theoretisch einfach, wird aber in der Praxis etwas komplexer, wenn Sie Ihre Drupal-Instanz in einer Shared Hosting-Umgebung wie Strato hosten. Hier ist die Art und Weise, wie SSL funktioniert, indem Sie einen gemeinsamen SSL Reverse Proxy zwischen Ihrem Webbrowser und Ihrem normalen (und unverschlüsselten) Drupal-Webserver einrichten.

Da sowohl der SSL Reverse Proxy als auch Ihr Drupal Server im gleichen, vertrauenswürdigen Netzwerk bei Ihrem Hosting-Provider Ihrer Wahl sitzen, ist dies sicher genug für eine kostengünstige Lösung.

Das Problemganz leicht in Strato beheben

Der Nebeneffekt beim Einfügen eines Reverse-Proxy zwischen Drupal und Ihren Benutzern ist, dass sich alle URLs ändern: Im Falle von Strato als Hoster wird http://constantin.glez.de zu https://www.ssl-id.de/constantin.glez.de, wobei www.ssl-id.de der SSL-Reverse-Proxy von Strato und constantin.glez.de der ursprüngliche Hostname ist, der nun in eine Pfadkomponente einer mit Reverse-Proxy manipulierten URL umgewandelt wird. Hässlich, aber praktikabel. Leider wird dies von Drupal nicht unbemerkt bleiben, und es treten einige Probleme auf:

Im Falle mehrerer Drupal-Instanzen, die darauf angewiesen sind, den Hostnamen zu kennen, um zwischen ihnen zu wählen, werden sie verwirrt sein, weil sie den Hostnamen des SSL-Reverse-Proxys in der HTTP_HOST-Variablen des Servers erhalten, nicht den ursprünglichen Hostnamen, mit dem die ursprüngliche Website angesprochen wurde. In meinem Fall hat Drupal www.ssl-id.de als Hostname bekommen, nicht constantin.glez.de aus dem Webserver, und so wusste es nicht, dass die Drupal-Instanz von constantin.glez.de vorgesehen war. Daher griff Drupal auf die Standardinstanz zurück, die ich nicht wollte.

Drupal generiert eine Handvoll relativer URLs für Bilder, CSS, Javascript etc. Um korrekt zu funktionieren, müssen diesen URLs der Hostname und das Protokoll des SSL-Proxys vorangestellt werden, während der ursprüngliche Hostname in den Pfadnamen eingearbeitet werden muss. Aber da Drupal nicht weiß, wie man hinter einem Reverse-Proxy sitzt, sind die generierten URLs falsch. Und ohne CSS, Bilder usw. sieht Ihre Website eher hässlich aus.

Drupal ist bereit, die Arbeit hinter einem Reverse-Proxy zu unterstützen, wenn es davon weiß, aber was ist mit gemischten Umgebungen? In unserem Fall möchten wir, dass alle Anmelde- und Admin-Aktionen SSL-verschlüsselt sind (aus Sicherheitsgründen) und in allen anderen Fällen (z.B. für regelmäßige Blog-Besucher) unverschlüsselt erreicht werden (aus Geschwindigkeitsgründen). Jetzt wird es kompliziert: Wie soll Drupal wissen, wann im Reverse-Proxy-Modus zu arbeiten ist und wann nicht?

Es gibt einige weitere Feinheiten im Zusammenhang mit der hinter einem Reverse-Proxy, dass Drupal muss sich um: Was ist der richtige Cookie-Host? Wie ist die IP des Reverse-Proxys, und können wir ihm vertrauen?

Die Lösung

Glücklicherweise waren 80% der Lösung bereits da, geschrieben in einem nützlichen Artikel namens „Drupal via HTTPS/SSL Proxy Server (shared certificates)“. Schauen Sie sich diese Lösung an, denn wir werden sie unten besprechen und wiederkommen, ich werde auf Sie warten.

Dies ist eine schöne, elegante Lösung, die vollständig in settings.php läuft, so dass sie kein Modul- oder Theme-Hacking benötigt. Aber nachdem ich es ausprobiert hatte, stieß ich immer noch auf ein paar Probleme:

Drupal hat immer noch nicht die richtige Instanz ausgewählt. In meinem Fall lebt die modifizierte settings.php in dem Verzeichnis, das der Instanz „constantin.glez.de“ von Drupal zugeordnet ist, aber die Anfrage kommt über „www.ssl-id.de“. Da der Hostname unterschiedlich ist, wird er nie wissen, dass er in das Verzeichnis „constantin.glez.de“ gehen sollte, um seine Konfiguration zu finden. Glücklicherweise erlaubt es uns die Art und Weise, wie Drupal nach seinen settings.php-Dateien sucht, nicht nur den Hostnamen, sondern auch Teile der Pfadkomponenten anzugeben.

Die nächste Ausgabe ist wahrscheinlich für jeden Hosting-Provider anders: „Skybow“, der Autor des ursprünglichen Tutorials, nutzte Hosteurope als Hosting-Provider. Anscheinend setzt ihr SSL Reverse-Proxy HTTP_X_FORWARDED_HOST beim Weiterleiten von Anfragen. Nicht im Falle von Strato. Aber wie können wir herausfinden, wie wir erkennen können, dass wir hinter dem SSL-Proxy stecken? Um zu sehen, was genau passiert, wenn Sie Ihre Drupal-Instanz aufrufen, ist es hilfreich, sich selbst eine Datei namens phpinfo.php zu erstellen, sagen Sie phpinfo.php und legen Sie sie in Ihren Hosting-Bereich, dann fügen Sie die folgende Zeile hinzu:

<?php phpinfo(); \>>

Der Aufruf dieses einfachen Skripts lässt PHP die gesamte Umgebung ausgeben, so dass Sie Ihren Code debuggen und nach Hinweisen in der Serverumgebung des Hosters suchen können. Tatsächlich hat phpinfo() bestätigt, dass HTTP_X_FORWARDED_HOST nicht einmal in der Strato-Umgebung vorhanden ist. Stattdessen habe ich herausgefunden, dass Strato SERVER_PORT entsprechend dem HTTP-Sicherheitsmodus setzt („80“ für reguläres HTTP, „443“ für HTTPS). Cool, jetzt hatte ich einen neuen Lackmustest für Skybow’s Code und sicher genug, um die erste Zeile zu ändern:

$request_type = ($_SERVER[‚SERVER_PORT‘] ==’443′) ? SSL‘ : ‚NONSSL‘;