greatestview

Themen und Projekte aus der Medienwelt

Alternative WordPress-Dateistrukturen für bessere Wartbarkeit und Sicherheit

Kategorien:

Ich weiß gar nicht mehr genau, wie es angefangen hat. Vielleicht war es ein Artikel bei t3n, in dem Workflows für größere WordPress-Projekte vorgestellt werden. Unter anderem wird dort Composer erwähnt, was ich vor ein paar Jahren mal benutzt und seitdem in meinem Hinterkopf in der Schublade »instabil und kompliziert« abgelegt habe. Oder der WordPress-Workflow von Henning Orth, der ein komplexes Entwicklungs- und Deployment-Setup beschreibt. Hauptsächlich war es aber wohl die seit Äonen bestehende WordPress-Dateistruktur, die zwar für Anfänger sehr praktisch ist, dafür aber einige entscheidende Nachteile mit sich bringt.

Die Standard-WordPress-Dateistruktur und ihre Nachteile

Schauen wir uns mal das Hauptverzeichnis einer frischen WordPress-Installation an:

./                        # Öffentlich zugänglicher Webroot
├── wp-admin/
│   └── ...
├── wp-content/           # Hier wird die Site individuell angepasst
│   ├── plugins/
│   ├── themes/
│   ├── uploads/
│   └── index.php
├── wp-includes/
│   └── ...
├── index.php
├── license.txt
├── readme.html
├── wp-activate.php
├── wp-blog-header.php
├── wp-comments-post.php
├── wp-config.php         # Hier wird die Site-Konfiguration gespeichert
├── wp-config-sample.php
├── wp-cron.php
├── wp-links-opml.php
├── wp-load.php
├── wp-login.php
├── wp-mail.php
├── wp-settings.php
├── wp-signup.php
├── wp-trackback.php
└── xmlrpc.php

Das ganze wird so 1 zu 1 auf den geneigten Server in den Webroot kopiert, fertig. Die Nachteile sind aber offensichtlich:

  1. Die Konfigurationsdatei wp-config.php mit den Datenbank-Zugangsdaten ist öffentlich zugänglich. Auch wenn WordPress Vorsichtsmaßnahmen ergreift, damit diese Datei keine Informationen preisgibt, handelt es sich dennoch um ein Anti-Pattern. Es besteht die Gefahr, dass bei bestimmter Server-Konfiguration der Inhalt der wp-config.php ausgelesen werden kann. Ausführlichere Argumente dazu gibts in diesem Post von Aaron Adams.
  2. Der WordPress-Core ist nicht getrennt von den Site-spezifischen Anpassungen und der Konfigurationsdatei sondern enthält diese. Das führt dazu, dass man bei Updates genau aufpassen muss, was ersetzt werden darf und was bleiben muss. Beliebtes Beispiel: individuell angelegte robots.txt oder .htaccess Dateien müssen neben WordPress-Dateien im Webroot liegen. Und wp-content/ darf dabei nicht versehentlich überschrieben werden.
  3. Generell ist es ziemlich unschön, wenn im Webroot WordPress- und andere Dateien vermischt werden. Dazu stelle man sich vor, dass es noch weitere Unterverzeichnisse für andere Projekte gibt, Chaos pur.

Das alles lies mich mal nach alternativen Strukturen umschauen.

Alternative WordPress-Dateistrukturen

The WordPress Way

Fangen wir mal mit der einfachsten Lösung an: WordPress selbst gibt einem Tipps, wie man die Dateistruktur verändern kann. Tatsächlich ist hier schon sehr viel mit Boardmitteln möglich! Die einfachste Variante ist es, die gesamte WordPress-Installation in ein eigenes Verzeichnis zu verschieben und die .htaccess-Datei etwas anzupassen. Das löst schon mal Punkt 3 der o. g. Liste. Wer sich weiter traut, kann sogar die Verzeichnisse wp-content, plugins, themes und uploads selbst festlegen. Die wp-config.php muss aber weiterhin im Webroot liegen bleiben, Problem Nummer 1 von oben ist damit also nicht gelöst. Immerhin ist es von Haus aus möglich, die wp-config.php im Webroot zu belassen, während der Core in ein Unterverzeichnis verlegt wird.

WordPress warnt übrigens davor, dass mit der neuen Struktur einige Plugins und Themes nicht mehr funktionieren können. Auch sind nicht alle Lösungen für ein Multisite-Setup geeignet. Dasselbe gilt auch für die anderen Lösungen.

WordPress-Schablonen auf Git-Basis

Ein beliebter Weg ist es, WordPress als Git Submodule einzubinden. WordPress selbst stellt nämlich ein Git-Repository bereit, welches sich alle 15 Minuten mit dem SVN synchronisiert, auf dem die eigentliche Entwicklung stattfindet. Darauf basiert z. B. das WordPress-Skeleton von Mark Jaquith. Zusammen mit ein paar Konfigurationen aus dem vorigen Abschnitt entsteht so eine Schablone, die zumindest die eingangs erwähnten Probleme Nummer 2 (Trennung des WP-Core vom Rest) und 3 (sauberer Webroot) lösen.

./                        # Öffentlich zugänglicher Webroot
├── content/              # Ehemals wp-content
│   ├── mu-plugins/
│   ├── plugins/
│   ├── themes/
│   ├── uploads -> ../shared/content/uploads/
│   └── index.php
├── shared/               # Verzeichnisse mit Schreibrechten
│   └── content/
│       └── uploads/
├── wp/                   # WordPress Core als Git Submodule
├── README.md
├── index.php
├── local-config-sample.php
├── local-config.php      # Anpassung für lokale Entwicklung
├── wp-cli.yml
└── wp-config.php         # Konfigurationsdatei

Da aber auch hier die wp-config.php noch im Webroot liegt und außerdem Git Submodules etwas komplexer zu handhaben sind, schauen wir mal weiter.

WordPress-Schablonen auf Composer-Basis

Hier wird es spannend. Vielleicht sollte ich zum Einstieg sagen, dass ich nach intensiver Auseinandersetzung meinen Frieden mit Composer geschlossen habe. Das ist schon ein nettes Tool, was sich ziemlich gemacht hat die letzten Jahre!

Kurzer Einschub: Composer ist ein Dependency Manager für PHP, ziemlich ähnlich zu npm von Node.js. Als Composer-Einführung habe ich auf die schnelle einen Artikel von Dave Smith aus dem Jahr 2014 gefunden, aber es gibt bestimmt noch weitere, bessere. Einfach mal googlen. Das was npmjs.com für npm ist, ist packagist.org für Composer.

Während Drupal 8 bereits auf eine Composer-basierte Struktur umgestellt hat, wird bei WordPress versucht, diese von außen dranzubauen. Dazu gibt es mehrere Zutaten:

Theoretisch kann man so schon selbst loslegen. Praktisch gibt es bereits viele Leute, die sich Gedanken zu einer perfekten Composer-basierten WordPress-Struktur gemacht haben. Das wohl bekannteste und, wie ich finde, beste Projekt dazu ist Bedrock von roots.

Bedrock

Bedrock bringt vieles mit, was ein modernes WordPress-Setup haben sollte, unter anderem die Lösung aller drei eingangs genannten Probleme: Die Konfiguration liegt außerhalb des Webroots, der WordPress-Core liegt gekapselt in einem Unterverzeichnis und die Site-spezifischen Anpassungen ebenfalls. Hooray! Damit hätten wir alle drei Probleme gelöst, oder?

Das ganze sieht dann in etwa so aus:

./
├── config/
│   ├── environments/     # Der Datenbank-Teil der wp-config.php
│   │   ├── development.php
│   │   ├── staging.php
│   │   └── production.php
│   └── application.php   # Ein Teil der wp-config.php ohne Datenbank-Daten
├── vendor/               # Composer dependencies
└── web/                  # Öffentlich zugänglicher Webroot
    ├── app/              # Ehemals wp-content
    │   ├── mu-plugins/
    │   ├── plugins/
    │   ├── themes/
    │   └── uploads/
    └── wp/               # WordPress core

Dazu bietet Bedrock noch ein paar Extras:

  • Composer-basiertes Management von Plugins, Themes, WP-Core etc.
  • Standardmäßig installiertes MU-Plugin wp-password-bcrypt. Warum das so wichtig ist, kann man hier nachlesen. Kurz: WordPress verwendet immer noch standardmäßig das unsichere MD5 zum Hashen von Passwörtern.
  • PHP-Coding-Standard PSR-2 statt WordPress-Coding-Standard.
  • Die Site-Konfiguration wird in env-Dateien gespeichert.
  • Einfache Anbindung an Deployment-Systeme (Trellis, Ansible, Vagrant, Capistrano…)

Damit liegt nun eine gute Auswahl an Möglichkeiten auf dem Tisch.

Schärfen der Anforderungen

Tja, was nehmen wir denn nu? Nachdem ich mich in alle möglichen Richtungen eingelesen habe und mir der Kopf rauchte, habe ich mich noch mal zurückgelehnt, um zu beschreiben, was ich eigentlich wirklich brauche. Neben den eingangs erwähnten drei Problemen soll mein zukünftiges System folgenden Anforderungen gerecht werden:

Close to Core

Diese Palasthotel-Philosophie hat uns schon ziemlich oft gerettet. Mehr Abhängigkeiten bedeuten einfach ein größeres Risiko. Meinen WordPress-Blog gibt es schon seit dem Jahr 2007; und der soll weiterhin so unkompliziert wartbar bleiben. Moderne Techniken kommen und gehen, heutzutage ist die Halbwertszeit erschreckend gering (ja, ich meine euch, JavaScript-Frameworks!)

Kein Build-Prozess

Auch wenn es gerade en vogue ist, beim Deployment einen Build-Prozess anzustoßen: Für meine Blogs wäre das Overkill und eine Wartungshölle. Man muss sich auf weitere Tools verlassen, gegebenenfalls sogar Online-Dienste, die alle ihre eigenen Abhängigkeiten mitbringen. Oder selbst ein funktionierendes Deployment-System bauen, welches z. B. den zeitlichen Ausfall während eines Builds berücksichtigt. Da bleibe ich lieber bei meinem Git-basierten Deployment.

Automatische WordPress-Updates

WordPress-Sicherheitsupdates sollen automatisch installiert werden. Und zwar nicht unbedingt über die interne Update-Mechanik, sondern via WP-CLI oder Composer. Eine nette Lösung dazu habe ich schon gefunden, Details dazu gibt es demnächst in einem eigenen Post.

Bedrock, ich habe leider kein Foto für dich

Nach langem Überlegen habe ich mich gegen Bedrock entschieden. Das sind die Gründe:

  • Unnötige Abstraktionen, die sich gegen den Close-to-Core-Gedanken richten. So wird z. B. für das Management der Konfigurationsdateien ein weiteres Paket eingebunden: Dotenv. Außerdem wird die gewohnte Konfiguration der wp-config.php auf mehrere Dateien verteilt (application.php plus Environment-Config).
  • PSR-2 Coding Standard: Ich finde, wenn ein System schon einen Coding Standard mitbringt, so wie es WordPress tut, dann sollte man sich auch daran halten.
  • Möchte ich ein WordPress-Setup mit Bedrock verwalten, gibt es mindestens vier zusätzliche Abhängigkeiten: Composer, Packagist für die Paketverwaltung, das JohnBloch-WordPress-Paket, Bedrock. Wird ein Build-Prozess benutzt, sind wir schon bei mindestens fünf Abhängigkeiten. Das ist mir für einen langfristigen Zeitraum von mehreren Jahren zu riskant.

Da der letzte Punkt quasi für alle Composer-Varianten gilt, habe ich mich letzten Endes auch davon verabschiedet. Composer wird einfach nicht offiziell von WordPress unterstützt. Wer weiß, vielleicht ändert sich das irgendwann mal. Aber genauso gut kann es passieren, dass einem der mühsam eingerichtete Composer-Workflow irgendwann bei einem WP-Core-Update um die Ohren fliegt.

Die ultimativ beste WordPress-Dateistruktur

Das war bisher ein weiter Weg. Wenn man sich die oben genannten Anforderungen anschaut, ist vermutlich schon klar, worauf es hinausläuft: Wir bauen uns ein System, das auf WordPress-Bordmitteln aufsetzt. Dazu inspiriert wurde ich durch Bedrock und den schon drei Jahre alten Artikel How And Why Install WordPress Core In A Subdirectory von Iain Poulson. Der Trick ist es, die Möglichkeiten aus dem obigen Abschnitt The WordPress Way geschickt zu kombinieren und an wenigen Stellen zu erweitern. Am Ende habe ich diese WordPress-Struktur erreicht:

./
├── config/
│   ├── wp-config.dev.php
│   ├── wp-config.php -> wp-config.dev.php  # Einfacher Symlink
│   └── wp-config.prod.php
└── public/               # Öffentlich zugänglicher Webroot
    ├── content/          # Ehemals wp-content
    │ ├── mu-plugins/
    │ ├── plugins/
    │ ├── themes/
    │ └── uploads/
    ├── wp/               # WordPress core
    ├── index.php         # Angepasste Kopie der /wp/index.php
    └── wp-config.php     # Inkludiert /config/wp-config.php

Wie man sieht, sind die wirklich »neuen« Elemente eine angepasste zusätzliche index.php und eine Fake-wp-config.php. Das Verzeichnis wp-content habe ich in content umbenannt, wie es WordPress-Skeleton auch macht, da es nun nicht mehr im WordPress-Verzeichnis liegt und kein Namespacing-Prefix mehr braucht.

Fassen wir noch mal zusammen: Wir haben mit diesem System die geringstmögliche Anzahl an Abhängigkeiten von Dritt-Systemen bei maximaler Wartbarkeit und Sicherheit. Der WordPress-Core liegt gekapselt in einem eigenen Verzeichnis, genauso wie die Site-spezifischen Anpassungen. Die Konfigurationsdateien liegen außerhalb des Webroots. Super!

Natürlich gibt es auch einen Nachteil, wenn man so will: Möchte man Updates automatisieren, muss man auf WP-CLI zurückgreifen. Das ist per se nicht schlecht und funktioniert auch bestens mit der neuen Struktur (wenn man nicht vergisst WP-CLI im Verzeichnis /webroot/wp auszuführen). Composer ist hier allerdings noch feiner einstellbar und benötigt nicht wie WP-CLI Zugriff auf die Datenbank.

WordPress-Blueprint auf GitHub

Ich habe das ganze System unter dem Namen WordPress-Blueprint auf GitHub veröffentlicht. Darin enthalten sind ein paar zusätzliche kleine Features, wie z. B. das oben erwähnte MU-Plugin wp-password-bcrypt und Sicherheitsanpassungen aus dem Hardening-WordPress-Handbuch. Der WordPress-Core ist zur Veranschaulichung als Git Submodule eingebunden. Wenn ihr den Core gleich mit herunterladen möchtet, klont euch das Repository einfach via:

$ git clone --recurse-submodules https://github.com/greatestview/WordPress-Blueprint.git

Schaut es euch gerne an, ich freue mich über Feedback!

Schreibe einen Kommentar

Kommentare werden moderiert, bitte bleib also nett und freundlich. Deine E-Mail-Adresse wird nicht veröffentlicht. Einfache HTML-Tags sind erlaubt.