Wie funktionieren Dateirechte?

Keywords: #chmod #dateirechte #sysop #wordpress

Die korrekten Dateirechte sollten eine wichtige Rolle spielen, wenn du an einem Sicherheitskonzept für deine Webseite bzw. Wordpress arbeitest. Leider wird das Thema Dateirechte oft stiefmütterlich behandelt, nicht zuletzt weil es fälschlicherweise als zu kompliziert angesehen wird. Dabei muss es das gar nicht sein: Das Grundprinzip ist einfach: Du willst nicht, dass jedermann Dateien ändern, ausführen oder lesen kann. Sicherlich gibt es feine Abstufungen. und genau die möchte ich hier erklären.

Achtung: Das Ziel dieses Beitrags ist nicht, dir eine fertige Lösung für deine WordPress-Installation bzw. dein Projekt zu geben. Das ist nicht möglich. Warum? Es gibt zig unterschiedliche Konfigurationen. Es mag allgemeingültige Lösungen geben, diese sind aber bei weitem nicht sicher. Stichwort: chmod 777 * -R

Du solltest nach der Lektüre aber das Konzept verstehen und vielleicht sogar in der Lage sein, die Dateirechte selbstständig zu setzen.

Die Grundlagen: Wie verarbeitet ein Web-Server Anfragen?

Jetzt fragst du dich sicherlich, was das mit Dateirechten zu tun hat? Was passiert, wenn du auf einer Webseite surfst? Folgende Vereinfachung dürfte das konzept hilfreich beschreibe: Der Web-Server schickt eine Datei an deinen Browser. Und du kannst Dateien an den Web-Server schicken, sprich hochladen. Wer also verstehen will, wie Dateirechte gesetzt werden müssen, sollte das Grundkonzept eines Servers verstehen.

Wie sieht diese Kommunikation mit dem Server also im Detail aus? Du schickst über deinen Browser eine Anfrage (http://nickyreinert.de/index.html) an den Server (was auch nur irgendein anderer Computer auf der Welt ist, egal ob in der Cloud oder in einem Keller). Auf dem Server laufen verschiedene Dienste - die verwirrenderweise auch als Server bezeichnet werden.

Bild

Die Cloud ist auch nur der Computer von jemand anders

Einer dieser Dienste ist der HTTP-Dienst. Sehr verbreitet sind Apache2 und nginx. Das Konzept ist bei beiden gleich: Sie empfangen deine Anfrage und schicken dir eine Antwort. Bei dieser Antwort unterscheidet man grob zwischen statische Inhalten (HTML-Datei, Texte, Bilder, …), die der HTTP-Dienst direkt zurückschickt und dynamischen Inhalten. Dynamische Inhalte müssen auf dem Server erst verarbeitet werden, wie z.B. Datenbankabfragen oder ganz simpel die Darstellung der aktuellen Uhrzeit: https://nickyreinert.de/dynamisch.php. Das ermöglicht ein PHP-Dienst bzw. PHP-Interpreter.

Im Kontext der Dateirechte muss man hier eine wichtige Unterscheidung treffen: Der PHP-Interpreter ist entweder Teil des HTTP-Dienstes (PHP-Modul). Damit ist z.B. Apache2 in der Lage, die PHP-Datei quasi selber zu interpretieren, und den dynamischen Inhalt für die Antwort zu erzeugen. Oder der PHP-Dienst ist ein getrennt laufender Dienst bzw. Prozess, der über eine Schnittstelle (CGI) vom HTTP-Dienst angesprochen wird (mehr dazu hier).

Um Informationen auf dem Server zu speichern, gibt es zwei Möglichkeiten: Zum einen wäre da die Datenbank, wie z.B. MySQL oder MariaDB. Der PHP-Dienst nutzt die Datenbank, um Informationen abzulegen, die in der Regel volatil sind. Wie z.B. Kommentare oder Texte für deinen Blog. Und es gibt das Dateisystem. Ganz banal ausgedrückt ist das die Festplatte des Servers. Und genau dort brauchst du Dateirechte. Die folgende Abbildung zeigt die beiden oben erwähnten Konfigurationen: In der ersten Konfiguration sind HTTP- und PHP-Dienst getrennte Dienste, in der zweiten Konfiguration nutzt der HTTP-Dienst PHP als integriertes Modul.

Die Abbildung zeigt außerdem ein beispielhaftes Dateisystem mit einigen ausgewählten Dateien und Ordnern, die dir von WordPress bekannt vorkommen dürften.

Der vierte Dienst im Bunde ist übrigens der FTP-Dienst. Dieser ermöglicht es (in der Regel dem Web-Entwickler) sich per FTP auf den Server zu verbinden und Dateien über einen FTP-Client hochzuladen, zu bearbeiten oder zu löschen.

Lesen, Schreiben, Ausführen - Was bedeutet das eigentlich?

Wenn es um die Fähigkeit “Ausführen” geht, muss man streng zwischen Ordnern und Dateien unterscheiden. Das Ausführen von Dateien, egal welche Endung sie haben, ist in keiner Konfiguration notwendig! Das Ausführen von Ordern hingegen ermöglicht den Zugriff auf den Ordner. Jeder Ordner deiner WordPress-Installation darf also ausführbar sein.

Das Lesen eines Ordners ermöglicht einem Dienst, die Ordner-Inhalte aufzulisten. Das Lesen einer Datei ermöglicht einem Dienst den Inhalt einer Datei darzustellen. Und das ist bereits ein sicherheitskritisches Merkmal: Nicht jeder Dienst muss bzw. darf jede Datei lesen. Der HTTP-Dienst benötigt z.B. keinen lesenden Zugriff auf PHP-Dateien. Und erst Recht nicht auf z.B. die wp-config.php - in der sich ja das Passwort für die Datenbank befindet. Der HTTP-Dienst reicht die Anfrage für die dynamische PHP-Datei in der Regel an den PHP-Dienst weiter. Gleichwohl muss der HTTP-Dienst statische Inhalte lesen können. Vor allem bei den Leserechten gibt es also sehr viele Feinheiten zu beachten.

Wer Schreibrechte für einen Ordner hat, kann darin Dateien anlegen bzw. den Ordner löschen oder umbenennen. Das Schreibrecht für eine Datei ermöglicht das Löschen der Datei und das Ändern der Inhalte. Grundsätzlich sollte kein Dienst Schreibrechte im Dateisystem besitzen. So vermeidet man z.B., dass jemand Schadcode in eine PHP-Datei einfügt. Der PHP-Dienst kann Informationen in der Datenbank ablegen und für den HTTP-Dienst gibt es erstmal keinen Grund, überhaupt Dateien ändern oder anlegen zu müssen.

Natürlich gibt es Ausnahmen. Z.B. benötigt der PHP-Dienst Schreibrechte, wenn Dateien von WordPress oder der Plugins aktualisiert werden müssen. Oder ein Nutzer Dateien hochladen möchte (Hier kommt übrigens eine andere wichtige Sicherheitseinstellung zum Tragen: Man kann unterbinden, dass der PHP-Dienst Dateien aus dem Upload-Ordner als PHP-Dateien verarbeitet.)

Wie wir diese Dateirechte setzen, dazu gleich mehr.

Besitzrechte einer Datei

Vorher schauen wir uns noch die Besitzverhältnisse von Dateien und Ordnern an. Diese lassen sich einem Besitzer und einer Gruppe zuordnen. Jeder der oben genannten Dienste kann, ganz abstrakt betracht, als Nutzer verstanden werden. Und damit entweder direkt Besitzer einer Datei oder eines Ordners sein. Oder indirekt, über die Gruppenzugehörigkeit.

Eine Datei gehört also zu einem Besitzer und zu einer Gruppe von Besitzern. Der Besitzer muss nicht zwingend Teil der Gruppe sein. Der Besitzer kann bestimmte Rechte für diese Datei besitzen, wie z.B. Lesen und Schreiben. Einer Gruppe von Besitzern werden auch individuelle Rechte zugesprochen, wie z.B. nur das Lesen. Und dann gibt es noch “alle anderen”, die vielleicht gar keine Zugriffsrechte auf eine Datei haben. Am Beispiel von index.html sieht das folgendermaßen aus:

index.html:

  • Besitzer: FTP-Dienst, darf lesen und schreiben,
  • Gruppe: WWW-Data, darf lesen,
  • alle andern: keine Rechte

Und das enstpricht auch schon der klassischen Rechtevergabe von Dateien. Schreibrechte werden nur dem Besitzer eingeräumt. Die Gruppe darf bestenfalls lesen. Natürlich gibt es Sonderfälle, wie das Aktualisieren von WordPress oder von Plugins, dazu später mehr.

Wie funktionieren chown und chmod?

Du wirst es sicher schon mal gehört haben: Dateirechte werden mit chown und chmod konfiguriert. chown steht für “change owner” - also “ändere den Besitzer” und chmod für “change mode”, “ändere den Modus”.

Zu Demonstrationszwecken gehen wir von der folgenden Konfiguration aus, die zwar bei weitem nicht die sicherste, dafür aber weit verbreitet ist:

Die Nutzer für den PHP- und HTTP-Dienst sind in der Gruppe www-data zusammengefasst. Daneben gibt es den FTP-Dienst (ftp-user).

Ftp-user bekommt alle Rechte: Lesen, Schreiben, usw. Die Nutzer der Gruppe www-data bekommen nur lesenden Zugriff - vorerst!

Jede Datei und jeder Ordner sollen dem ftp-user gehören, gleichzeitig aber auch zur Gruppe www-data. Das lässt sich mit chown folgendermaßen bewerkstelligen:

chown ftp-user:www-data /var/nginx/www/* --recursive

Der erste Parameter definiert den Besitzer (ftp-user) und die Gruppenzugehörigkeit (www-data), immer getrent mit einem Doppelpunkt. Danach folgt der betroffene Ordner bzw. die Datei. Mit --recursive (oder kürzer -R) werden auch alle Unterordner und darin befindlichen Dateien angesprochen.

Bei den Dateirechten wird es etwas aufwendiger, zunächst der Aufruf:

chmod 640 /var/nginx/www/* -R

Hier nutzen wir die Kurzschreibweise -R um alle Dateien und Ordner rekursiv zu ändern. Wichtig ist für uns die Ziffer hinter chmod: 640. Genau genommen handelt es sich um drei (oktale) Ziffern, nämlich 6, 4 und 0. Entsprechend der Position regeln sie die Zugriffsrechte für den

  • Besitzer (ftp-user, die 6 an erster Stelle),
  • die Gruppe (www-data) samt ihrer Mitglieder (HTTP-Dienst, die 4 an zweiter Stelle)
  • und an letzter Stelle alle anderen (others - die 0).

Und wie entstehen die Ziffern 6, 4 und 0? Wir erinnern uns an die drei Zugriffsrechte:

  • lesen,
  • schreiben sowie
  • ausführen.

Jedes Recht besitzt einen Wert:

  • Lesen (bzw. r für read) = 4
  • Schreiben (bzw. w für write) = 2
  • Ausführen (bzw. x für eXecute) = 1
  • Kein Recht = 0

Die Berechnung der Dateirechte für chmod

Die Berechnung der Dateirechte für chmod

Um Rechte zu kombinieren, bildet man deren Summe. Will man also alle Rechte vergeben, erechnet sich das folgendermaßen:

  • Lesen + Schreiben + Ausführen = 4 + 2+ 1 = 7

Nur Lese- und Schreibzugriff errechnet sich demnach so:

  • Lesen + Schreiben = 6

Die obene genannte chmod-Anweisung setzt also die folgenden Rechte um:

  • Besitzer (ftp-user): 6 (Lesen, Schreiben)
  • Gruppe (www-data): 4 (Lesen)
  • Alle anderen (others): 0 (keine Rechte)

Was heißt das? Der FTP-Nutzer darf als Besitzer alles: Dateien hochladen, löschen, bearbeiten usw. Jedes Mitglied der Gruppe www-data darf Dateien nur lesen. Jeder andere Nutzer (bzw. Dienst) darf gar nicht auf die Dateien in deinem WordPress-Projekt zugreifen. Das klingt sehr sicher, führt allerdings zu einem Problem: Dein Server dürfte so nicht funktionieren. Nimm als Beispiel diesen Aufruf: https://nickyreinert.de/ordner/

Die HTTP-Fehlermeldung 403 Forbidden erscheint. Das sind die Zugriffsrechte für den Ordner, so wie wir sie eben gesetzt haben:

drw-r----- 2 ftp-user www-data   4096 Okt 14 11:48 ordner/

In der zweiten Spalte sehen wir: Der Ordner gehört ftp-user und der Gruppe www-data.

Das d ganz am Anfang steht für directory, lies Ordner. Dann folgen die Dateirechte, die wir eben schon festgelegt haben: Zunächst drei Zeichen, die die Zugriffsrechte für den Besitzer (ftp-user) beschreiben: rw-

Also Lesen (read) und Schreiben (write) (würde anstelle des - ein x stehen (eXecute), stünde das für Ausführen). Die Gruppe www-data und alle ihre Mitglieder dürfen nur Lesen (r–). Alle anderen dürfen gar nichts (—).

Nun handelt es sich aber um einen Ordner. Ein Ordner kann aber nur “betreten” werden, wenn man die Genehmigung zum Ausführen hat. Weder der Besitzer noch die Gruppe hat diese Genehmigung. Unsere obere Anweisung reicht also nicht aus! Tatsächlich sollte man also Zugriffsrechte für Dateien und Ordner immer getrennt festlegen!

Berechtigung für alle Dateien:

chmod 640 /var/nginx/www/* -R

Berechtigung für alle Ordner:

chmod 750 /var/nginx/www/* -R

Du siehst, dass das Ermöglichen des Ausführens einfach durch das Addieren von 1 für die beiden ersten Positionen ermöglicht wird.

Tatsächlich findest du diese beiden Befehle auch in vielen Anleitungen zu Dateirechten und damit endet die Anleitung oft auch schon. Doch damit hast du ein Problem. Denn die zweite Anweisung überschreibt das Ergebnis der ersten Anweisung. Aber wir wollen doch Dateien und Ordner individuell einrichten! Abhilfe schafft der Befehl find, um Dateien und Ordner getrennt anzusprechen:

find /var/nginx/htdocs/. -type f -exec chmod 640 -- {} +
find /var/nginx/htdocs/. -type d -exec chmod 750 -- {} +

Der Parameter -type f liefert alle Dateien im angegeben Ordner zurück (f wie file, Datei). Der Punkt hinter htdocs in der ersten Zeile sorgt dafür, dass auch versteckte Dateien, wie z.B. die .htaccess-Datei, gefunden werden. Mit exec übergebe ich das Resultat von find, eine Liste die alle gefundenen Dateien enthält, an einen anderen Befehl. In dem Fall also chmod.

Vice versa für alle Ordner, die mit -type d gefunden werden. Hier wird mit chmod auch das Recht zum Ausführen vergeben.

Dateirechte in der Realität

Das klingt erstmal recht einfach, hat aber drei Haken:

  1. Es gibt Dateien, die nur für den PHP-Dienst relevant sind, andere wiederum nur für den HTTP-Dienst. Man muss die Besitzrechte also etwas granularer vergeben.
  2. Bei einem Update der Plugins oder WordPress müssen Dateien für die Gruppe www-data wenigstens temporär schreibbar sein.
  3. Wenn man den Upload von Dateien zulassen möchte, muss der Ordner /wp-content/uploads dauerhaft beschreibbar sein.

Es gibt also verschiedene Ordner- und Datei-Kategorien, die verschiedene Rechte erfordern. Die folgende Matrix zeigt, am Beispiel von WordPress, eine ideale Konfiguration:

Die **.htaccess-**Datei muss für den PHP-Dienst gar nicht sichtbar, geschweige denn beschreibbar sein. Die Datei wp-config.php wiederum sollte für den HTTP-Dienst unsichtbar sein. Generell müssen PHP-Dateien, also dynamische Ressourcen, für den HTTP-Dienst gar nicht sichtbar sein. Der Upload-Ordner ist hier beschreibbar, um das Hochladen von Datein zu ermöglichen. Wird ein Cache verwendet, muss natürlich auch dieser Ordner beschreibbar sein.

Solltest du, z.B. auf deinem eigenen Webspace, die Möglichkeit haben, Besitzrechte derart granular zu konfigurieren, kann dir die Matrix vielleicht als Inspiration dienen. In der Realität hilft das aber nicht weiter, da der PHP-Dienst als Modul entweder innerhalb des HTTP-Dienstes läuft oder beide Dienste unter einer Gruppe (www-data) laufen (hier wird das Problem noch etwas genauer beleuchtet). Die Konfiguration sieht dann in etwa so aus:

Nun stellst du dir sicher die Frage, wie du ein derartiges Setup möglichst unkompliziert einrichten kannst. Eins Vorweg: Ohne Zugriff auf die Shell wird das ziemlich aufwendig. Mit Zugriff auf die Kommandozeile kannst du die Dateirechte z.B. folgendermaßen setzen:

find /var/nginx/htdocs/. -type f -exec chmod 640 -- {} +
find /var/nginx/htdocs/. -type d -exec chmod 750 -- {} +
find /var/nginx/htdocs/wp-content/uploads/. -type f -exec chmod 660 -- {} +
find /var/nginx/htdocs/wp-content/uploads/. -type d -exec chmod 770 -- {} +
find /var/nginx/htdocs/wp-content/cache/. -type f -exec chmod 660 -- {} +
find /var/nginx/htdocs/wp-content/cache/. -type d -exec chmod 770 -- {} +

Wenn du Themes oder Plugins aktualisieren willst, musst du die Rechte folgendermaßen anpassen und somit Datein und Ordner schreibbar machen:

find /var/nginx/htdocs/wp-content/themes/. -type f -exec chmod 660 -- {} +
find /var/nginx/htdocs/wp-content/themes/. -type d -exec chmod 770 -- {} +
find /var/nginx/htdocs/wp-content/plugins/. -type f -exec chmod 660 -- {} +
find /var/nginx/htdocs/wp-content/plugins/. -type d -exec chmod 770 -- {} +

Beim Update von WordPress trifft das natürlich auf die ganze Installation zu:

find /var/nginx/htdocs/. -type f -exec chmod 660 -- {} +
find /var/nginx/htdocs/. -type d -exec chmod 770 -- {} +

Wenn du keinen Zugriff auf die Kommandozeile hast, kannst du auch PHP nutzen, insofern du die Rechte dazu hast. Die Funktion lautet chmod().

Ausführen von PHP-Code in bestimmten Ordnern unterbinden

Oben hatte ich das Problem mit dem Upload-Ordner angesprochen. Wenn Nutzer in der Lage sind, dort Dateien zu modifizieren oder abzulegen, ist das ein Sicherheitsrisiko. Du kannst das Risiko aber eindämmen, indem du dafür sorgst, dass Dateien in diesem Ordner schlicht nicht von PHP interpretiert, sprich ausgeführt, werden können.

So kann der Ordner beschreibbar bleiben, das Sicherheitsrisiko wird aber trotzdem reduziert. Dazu erstellst du eine .htaccess-Datei im uploads-Ordner mit diesem Inhalt:

SetHandler ! RemoveHandler .php .phtml .php3 RemoveType .php .phtml .php3 php_admin_value engine Off php_flag engine off

Die beiden letzten Zeile sind redundant zu den vorherigen. Sie soll sicherstellen, dass PHP hier wirklich nicht funktioniert. Ein Angreifer ist somit vielleicht in der Lage, PHP-Code in diesem Ordner abzulegen aber nicht mehr, diesen auch auszuführen.

Bei nginx kannst du den Upload-Ordner einfach implizit von PHP abnabeln:

if ($uri !~ "^/uploads/") {

   fastcgi_pass unix:/run/php/php-fpm-$server.sock;

}

FS_METHOD

Wenn du meinst, die Dateirechte korrekt vergeben zu haben und trotzdem fordert dich Wordpress beim Updaten auf, die FTP-Zugangsdaten einzugeben, setze diesen Parameter in der Datei wp-config.php:

define(‘FS_METHOD’, ‘direct’);

WordPress weiß nun, dass es Dateien direkt anfassen darf, ohne den Umweg über den FTP-Nutzer zu gehen.

Dieser Parameter kann recht nützlich sein, wenn du die Dateirechte noch restriktiver vergeben willst, sprich: PHP gar keine Schreibrechte einräumst. Du musst dann einen FTP-Nutzer anlegen, über den WordPress sozusagen indirekt auf das Dateisystem zugreifen kann.

Wenig Aufwand, viel Nutzen

Die Einarbeitung in die Benutzerrechte mit chmod mag anfangs vielleicht ungewohnt sein, mit etwa Übung hat man den Dreh aber recht schnell raus. Der Nutzen ist immens, denn die falsche Rechte-Vergabe ist ein häufiger Grund, warum bösartige Software sich überhaupt auf einem Server verbreiten kann. Es gibt noch eine Handvoll andere Maßnahmen, die relativ simpel und effektiv sind, denen ich mich in einem anderen Beitrag widmen möchte.

Zuguter Letzt: Willst du wissen, ob die Dateirechte in deiner Installation korrekt gesetzt wurden, kannst du auf dieses Script zurückgreifen. Ich habe dazu das Script von Ernesto Ruge etwas überarbeitet, in der Funktionalität aber unverändert gelassen:

Die korrekten Datei und Ordner-Rechte für WordPress prüfen

testFileFolderPermissions.php_-1Herunterladen