Sicheres Hosten mehrerer Websites mit Nginx und Php-fpm unter Ubuntu 14.04

Einführung

Es ist bekannt, dass der LEMP-Stack (Linux, Nginx, MySQL, PHP) unerreichte Geschwindigkeit und Zuverlässigkeit für die Ausführung von PHP-Sites bietet. Andere Vorteile dieses beliebten Stacks wie Sicherheit und Isolation sind jedoch weniger beliebt.

In diesem Artikel zeigen wir Ihnen die Sicherheits- und Isolationsvorteile der Ausführung von Sites auf LEMP mit verschiedenen Linux-Benutzern. Dies erfolgt durch Erstellen verschiedener PHP-FPM-Pools für jeden Nginx-Serverblock (Site oder virtueller Host).

Voraussetzungen

Dieses Handbuch wurde unter Ubuntu 14.04 getestet. Die beschriebene Installation und Konfiguration ist unter anderen Betriebssystemen oder Betriebssystemversionen ähnlich, die Befehle und der Speicherort der Konfigurationsdateien können jedoch variieren.

Es wird auch davon ausgegangen, dass Sie bereits Nginx und PHP-FPM eingerichtet haben. Wenn nicht, folgen Sie bitte den Schritten eins und drei aus dem Artikel https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-ubuntu-14 -04 [So installieren Sie den Linux-, Nginx-, MySQL-, PHP- (LEMP-) Stack unter Ubuntu 14.04].

Alle Befehle in diesem Lernprogramm sollten als Benutzer ohne Rootberechtigung ausgeführt werden. Wenn für den Befehl root-Zugriff erforderlich ist, wird vor + sudo + angezeigt. Wenn Sie das noch nicht eingerichtet haben, folgen Sie diesem Tutorial: Initial Server Setup with Ubuntu 14.04 .

Sie benötigen außerdem einen vollqualifizierten Domainnamen (fqdn), der zum Testen auf das Droplet verweist, zusätzlich zum Standard-+ localhost +. Wenn Sie keinen zur Hand haben, können Sie "+ site1.example.org " verwenden. Bearbeiten Sie die Datei " / etc / hosts " mit Ihrem bevorzugten Editor wie folgt: " sudo vim / etc / hosts " und fügen Sie diese Zeile hinzu (ersetzen Sie " site1.example.org +" durch Ihre fqdn, wenn Sie sie verwenden):

/ etc / hosts

...
127.0.0.1 site1.example.org
...

Gründe, LEMP zusätzlich zu sichern

Unter einem gemeinsamen LEMP-Setup gibt es nur einen php-fpm-Pool, in dem alle PHP-Skripte für alle Sites unter demselben Benutzer ausgeführt werden. Dies wirft zwei Hauptprobleme auf:

  • Wenn eine Webanwendung auf einem Nginx-Server blockiert, d.h. Wenn eine Subdomain oder eine separate Site kompromittiert wird, sind auch alle Sites in diesem Droplet betroffen. Der Angreifer kann die Konfigurationsdateien einschließlich der Datenbankdetails der anderen Sites lesen oder sogar deren Dateien ändern.

  • Wenn Sie einem Benutzer Zugriff auf eine Site in Ihrem Droplet gewähren möchten, gewähren Sie ihm praktisch Zugriff auf alle Sites. Beispielsweise muss Ihr Entwickler an der Staging-Umgebung arbeiten. Selbst mit sehr strengen Dateiberechtigungen erhalten Sie jedoch weiterhin Zugriff auf alle Websites, einschließlich Ihrer Hauptwebsite, auf demselben Droplet.

Die obigen Probleme werden in php-fpm gelöst, indem für jede Site ein anderer Pool erstellt wird, der unter einem anderen Benutzer ausgeführt wird.

[[step-1-- configuring-php-fpm]] === Schritt 1 - Konfigurieren von PHP-FPM

Wenn Sie die Voraussetzungen erfüllt haben, sollten Sie bereits eine funktionierende Website im Droplet haben. Sofern Sie keinen benutzerdefinierten fqdn dafür angegeben haben, sollten Sie in der Lage sein, unter dem fqdn + localhost + lokal oder über die IP des Droplets remote darauf zuzugreifen.

Jetzt erstellen wir eine zweite Site (site1.example.org) mit einem eigenen PHP-FPM-Pool und einem Linux-Benutzer.

Beginnen wir mit der Erstellung des erforderlichen Benutzers. Für eine optimale Isolierung sollte der neue Benutzer eine eigene Gruppe haben. Also erstelle die Benutzergruppe + site1 +:

sudo groupadd site1

Dann erstelle bitte eine Benutzerseite1, die zu dieser Gruppe gehört:

sudo useradd -g site1 site1

Bisher hat der neue Benutzer site1 kein Kennwort und kann sich nicht beim Droplet anmelden. Wenn Sie jemandem direkten Zugriff auf die Dateien dieser Site gewähren möchten, sollten Sie mit dem Befehl "+ sudo passwd site1 +" ein Kennwort für diesen Benutzer erstellen. Mit der neuen Benutzer- / Kennwortkombination kann sich ein Benutzer remote per ssh oder sftp anmelden. Weitere Informationen und Sicherheitsdetails finden Sie im Artikel Einrichten eines sekundären SSH / SFTP-Benutzers mit eingeschränktem Verzeichniszugriff.

Erstellen Sie als Nächstes einen neuen PHP-FPM-Pool für site1. Ein PHP-FPM-Pool ist im Grunde genommen nur ein gewöhnlicher Linux-Prozess, der unter bestimmten Benutzern / Gruppen ausgeführt wird und auf einem Linux-Socket lauscht. Es könnte auch eine IP: Port-Kombination überwachen, dies würde jedoch mehr Droplet-Ressourcen erfordern und ist nicht die bevorzugte Methode.

Standardmäßig sollte in Ubuntu 14.04 jeder php-fpm Pool in einer Datei im Verzeichnis "+ / etc / php5 / fpm / pool.d " konfiguriert sein. Jede Datei mit den Endungen " .conf +" in diesem Verzeichnis wird automatisch in die globale Konfiguration von php-fpm geladen.

Erstellen wir also für unsere neue Site eine neue Datei "+ / etc / php5 / fpm / pool.d / site1.conf +". Sie können dies mit Ihrem Lieblingseditor wie folgt tun:

sudo vim /etc/php5/fpm/pool.d/site1.conf

Diese Datei sollte enthalten:

/etc/php5/fpm/pool.d/site1.conf

[site1]







pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /

Beachten Sie in der obigen Konfiguration diese spezifischen Optionen:

  • + [site1] + ist der Name des Pools. Für jeden Pool müssen Sie einen eindeutigen Namen angeben.

  • + user und` + group` stehen für den Linux-Benutzer und die Gruppe, unter der der neue Pool ausgeführt wird.

  • + listen + sollte auf einen eindeutigen Ort für jeden Pool verweisen.

  • + listen.owner + und + listen.group + definieren den Besitz des Listeners, d. h. die steckdose des neuen php-fpm pool. Nginx muss diesen Socket lesen können. Aus diesem Grund wird der Socket mit dem Benutzer und der Gruppe erstellt, unter denen nginx ausgeführt wird - "+ www-data +".

  • Mit + php_admin_value + können Sie benutzerdefinierte PHP-Konfigurationswerte festlegen. Wir haben damit Funktionen deaktiviert, die Linux-Befehle ausführen können - + exec, passthru, shell_exec, system +.

  • + php_admin_flag + ist ähnlich zu + php_admin_value +, aber es ist nur ein Schalter für boolesche Werte, d.h. an und aus. Wir deaktivieren die PHP-Funktion "+ allow_url_fopen +", mit der ein PHP-Skript entfernte Dateien öffnen kann und die vom Angreifer verwendet werden kann.

Die Optionen "+ pm +" befinden sich außerhalb des aktuellen Sicherheitsthemas. Sie sollten jedoch wissen, dass Sie damit die Leistung des Pools konfigurieren können.

Die Option "+ chdir " sollte " / " lauten. Dies ist das Stammverzeichnis des Dateisystems. Dies sollte nur geändert werden, wenn Sie eine andere wichtige Option " chroot +" verwenden.

Die Option "+ chroot +" ist in der obigen Konfiguration absichtlich nicht enthalten. Es würde Ihnen erlauben, einen Pool in einer inhaftierten Umgebung zu betreiben, d. H. in einem Verzeichnis gesperrt. Dies dient der Sicherheit, da Sie den Pool im Webstamm der Site sperren können. Diese ultimative Sicherheit wird jedoch ernsthafte Probleme für jede anständige PHP-Anwendung verursachen, die auf System-Binärdateien und Anwendungen wie Imagemagick beruht, die nicht verfügbar sein werden. Wenn Sie sich für dieses Thema interessieren, lesen Sie bitte den Artikel https://www.digitalocean.com/community/tutorials/how-to-use-firejail-to-set-up-a-wordpress-installation-in-a- Jailed-Umgebung [So richten Sie mit Firejail eine WordPress-Installation in einer Jailed-Umgebung ein].

Wenn Sie mit der obigen Konfiguration fertig sind, starten Sie php-fpm neu, damit die neuen Einstellungen mit dem folgenden Befehl wirksam werden:

sudo service php5-fpm restart

Stellen Sie sicher, dass der neue Pool ordnungsgemäß ausgeführt wird, indem Sie nach den folgenden Prozessen suchen:

ps aux |grep site1

Wenn Sie die genauen Anweisungen bis hierher befolgt haben, sollten Sie eine Ausgabe ähnlich der folgenden sehen:

  14042  0.0  0.8 133620  4208 ?        S    14:45   0:00 php-fpm: pool site1
  14043  0.0  1.1 133760  5892 ?        S    14:45   0:00 php-fpm: pool site1

In rot ist der Benutzer, unter dem der Prozess oder der php-fpm-Pool ausgeführt wird - site1.

Außerdem deaktivieren wir das von opcache bereitgestellte standardmäßige PHP-Caching. Diese spezielle Caching-Erweiterung ist zwar für die Leistung von Vorteil, jedoch nicht für die Sicherheit, wie wir später sehen werden. Um es zu deaktivieren, bearbeiten Sie die Datei "+ /etc/php5/fpm/conf.d/5-opcache.ini" mit Superuser-Rechten und fügen Sie die folgende Zeile hinzu:

/etc/php5/fpm/conf.d/05-opcache.ini

opcache.enable=0

Starten Sie dann php-fpm erneut (+ sudo service php5-fpm restart +), damit die Einstellung wirksam wird.

[[step-2-- configuring-nginx]] === Schritt 2 - Konfigurieren von Nginx

Sobald wir den php-fpm-Pool für unsere Site konfiguriert haben, konfigurieren wir den Serverblock in nginx. Zu diesem Zweck erstellen Sie bitte eine neue Datei "+ / etc / nginx / sites-available / site1 +" mit Ihrem bevorzugten Editor wie folgt:

sudo vim /etc/nginx/sites-available/site1

Diese Datei sollte enthalten:

/ etc / nginx / sites-available / site1

server {
   listen 80;


   index index.php index.html index.htm;



   location / {
       try_files $uri $uri/ =404;
   }

   location ~ \.php$ {
       try_files $uri =404;
       fastcgi_split_path_info ^(.+\.php)(/.+)$;

       fastcgi_index index.php;
       fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
       include fastcgi_params;
   }
}

Der obige Code zeigt eine allgemeine Konfiguration für einen Serverblock in nginx. Beachten Sie die interessanten hervorgehobenen Teile:

  • Das Webstammverzeichnis lautet "+ / usr / share / nginx / sites / site1 +".

  • Der Servername verwendet das fqdn "+ site1.example.org +", das in den Voraussetzungen dieses Artikels erwähnt wird.

  • + fastcgi_pass + spezifiziert den Handler für die PHP-Dateien. Für jede Site sollten Sie einen anderen Unix-Socket verwenden, z. B. "+ / var / run / php5-fpm-site1.sock +".

Erstellen Sie das Webstammverzeichnis:

sudo mkdir /usr/share/nginx/sites
sudo mkdir /usr/share/nginx/sites/site1

Um die obige Site zu aktivieren, müssen Sie einen Symlink dazu im Verzeichnis "+ / etc / nginx / sites-enabled / +" erstellen. Dies kann mit dem Befehl erfolgen:

sudo ln -s /etc/nginx/sites-available/site1 /etc/nginx/sites-enabled/site1

Starten Sie schließlich nginx neu, damit die Änderung wie folgt wirksam wird:

sudo service nginx restart

[[step-3-- testing]] === Schritt 3 - Testen

Zum Ausführen der Tests verwenden wir die bekannte Funktion phpinfo, die detaillierte Informationen zur PHP-Umgebung bietet. Erstellen Sie eine neue Datei unter dem Namen "+ info.php ", die nur die Zeile " <? Php phpinfo ()" enthält. ?> + `. Sie benötigen diese Datei zunächst auf der Standard-Nginx-Site und deren Web-Stammverzeichnis "+ / usr / share / nginx / html / +". Zu diesem Zweck können Sie einen Editor wie diesen verwenden:

sudo vim /usr/share/nginx/html/info.php

Kopieren Sie anschließend die Datei wie folgt in das Webstammverzeichnis der anderen Site (site1.example.org):

sudo cp /usr/share/nginx/html/info.php /usr/share/nginx/sites/site1/

Jetzt können Sie den grundlegendsten Test ausführen, um den Serverbenutzer zu überprüfen. Sie können den Test mit einem Browser oder über das Droplet-Terminal und lynx, den Befehlszeilenbrowser, durchführen. Wenn Sie noch keinen Luchs auf Ihrem Droplet haben, installieren Sie ihn mit dem Befehl "+ sudo apt-get install lynx +".

Überprüfen Sie zuerst die Datei "+ info.php +" von Ihrer Standard-Site. Es sollte unter localhost folgendermaßen erreichbar sein:

lynx --dump http://localhost/info.php |grep 'SERVER\["USER"\]'

Im obigen Befehl filtern wir die Ausgabe mit grep nur für die Variable "+ SERVER [" USER "] ", die für den Serverbenutzer steht. Für die Standard-Site sollte die Ausgabe den Standard-Benutzer " www-data" wie folgt anzeigen:

_SERVER["USER"]                 www-data

Überprüfen Sie als Nächstes den Serverbenutzer auf site1.example.org:

lynx --dump http://site1.example.org/info.php |grep 'SERVER\["USER"\]'

Sie sollten diese Zeit in der Ausgabe des Benutzers + site1 + sehen:

_SERVER["USER"]                 site1

Wenn Sie benutzerdefinierte PHP-Einstellungen für jeden PHP-FPM-Pool vorgenommen haben, können Sie die entsprechenden Werte auf die oben beschriebene Weise überprüfen, indem Sie die Ausgabe filtern, die Sie interessiert.

Bisher wissen wir, dass unsere beiden Sites unter unterschiedlichen Benutzern ausgeführt werden. Nun wollen wir uns ansehen, wie eine Verbindung gesichert wird. Um das Sicherheitsproblem zu veranschaulichen, das wir in diesem Artikel lösen, erstellen wir eine Datei mit vertraulichen Informationen. Normalerweise enthält eine solche Datei die Verbindungszeichenfolge zur Datenbank und die Benutzer- und Kennwortdetails des Datenbankbenutzers. Wenn jemand diese Informationen herausfindet, kann die Person mit der zugehörigen Site alles tun.

Erstellen Sie mit Ihrem bevorzugten Editor eine neue Datei auf Ihrer Hauptseite: "+ / usr / share / nginx / html / config.php +". Diese Datei sollte enthalten:

/usr/share/nginx/html/config.php

<?php
$pass = 'secret';
?>

In der obigen Datei definieren wir eine Variable mit dem Namen "+ pass ", die den Wert " secret" enthält. Natürlich möchten wir den Zugriff auf diese Datei einschränken. Daher setzen wir die Berechtigungen auf 400, sodass der Eigentümer der Datei nur Lesezugriff hat.

Führen Sie den folgenden Befehl aus, um die Berechtigungen auf 400 zu ändern:

sudo chmod 400 /usr/share/nginx/html/config.php

Unsere Hauptseite läuft auch unter dem Benutzer "+ www-data +", der diese Datei lesen kann. Ändern Sie daher den Besitz der Datei in diesen Benutzer wie folgt:

sudo chown www-data:www-data /usr/share/nginx/html/config.php

In unserem Beispiel verwenden wir eine andere Datei mit dem Namen "+ / usr / share / nginx / html / readfile.php +", um die geheimen Informationen zu lesen und auszudrucken. Diese Datei sollte den folgenden Code enthalten:

/usr/share/nginx/html/readfile.php

<?php
include('/usr/share/nginx/html/config.php');
print($pass);
?>

Ändern Sie auch den Eigentümer dieser Datei in "+ www-data":

sudo chown www-data:www-data /usr/share/nginx/html/readfile.php

Um zu bestätigen, dass alle Berechtigungen und Eigentumsrechte im Webstamm korrekt sind, führen Sie den Befehl + ls -l / usr / share / nginx / html / + aus. Sie sollten eine Ausgabe ähnlich der folgenden sehen:

-r-------- 1 www-data www-data  27 Jun 19 05:35 config.php
-rw-r--r-- 1 www-data www-data  68 Jun 21 16:31 readfile.php

Greifen Sie jetzt mit dem Befehl "+ lynx --dump http: // localhost / readfile.php " auf die zuletzt genannte Datei auf Ihrer Standard-Site zu. Sie sollten in der Ausgabe " secret +" gedruckt sehen können, was zeigt, dass auf die Datei mit vertraulichen Informationen innerhalb derselben Site zugegriffen werden kann, was dem erwarteten korrekten Verhalten entspricht.

Kopieren Sie nun die Datei "+ / usr / share / nginx / html / readfile.php +" auf Ihre zweite Site "site1.example.org" wie folgt:

sudo cp /usr/share/nginx/html/readfile.php /usr/share/nginx/sites/site1/

Um die Site- / Benutzerbeziehungen in Ordnung zu halten, stellen Sie sicher, dass die Dateien innerhalb jeder Site dem jeweiligen Site-Benutzer gehören. Ändern Sie dazu den Besitz der neu kopierten Datei mit dem folgenden Befehl in site1:

sudo chown site1:site1 /usr/share/nginx/sites/site1/readfile.php

Um zu bestätigen, dass Sie die richtigen Berechtigungen und Eigentumsrechte für die Datei festgelegt haben, listen Sie den Inhalt des site1-Webstamms mit dem Befehl "+ ls -l / usr / share / nginx / sites / site1 / +" auf. Das solltest du sehen:

-rw-r--r-- 1 site1 site1  80 Jun 21 16:44 readfile.php

Versuchen Sie dann, über site1.example.com mit dem Befehl + lynx --dump http: // site1.example.org / readfile.php + auf dieselbe Datei zuzugreifen. Es wird nur der leere Bereich angezeigt, der zurückgegeben wird. Wenn Sie mit dem grep-Befehl + sudo grep error / var / log / nginx / error.log + nach Fehlern im Fehlerprotokoll von nginx suchen, werden Sie außerdem Folgendes sehen:

2015/06/30 15:15:13 [error] 894#0: *242 FastCGI sent in stderr: "PHP message: PHP Warning:  include(/usr/share/nginx/html/config.php): failed to open stream: Permission denied in /usr/share/nginx/sites/site1/readfile.php on line 2

Die Warnung zeigt, dass ein Skript von der Site site1.example.org die vertrauliche Datei + config.php + von der Hauptsite nicht lesen kann. Daher können Websites, die unter verschiedenen Benutzern ausgeführt werden, die Sicherheit der anderen nicht gefährden.

Wenn Sie zum Ende des Konfigurationsteils dieses Artikels zurückkehren, werden Sie feststellen, dass wir das von opcache bereitgestellte Standardcaching deaktiviert haben. Wenn Sie neugierig sind, warum, versuchen Sie, den Opcache erneut zu aktivieren, indem Sie in der Datei "+ / etc / php5 / fpm / conf.d / 05-opcache.ini " die Superuser-Berechtigungen " opcache.enable = 1 " festlegen und neu starten php5-fpm mit dem Befehl ` sudo service php5-fpm restart +`.

Erstaunlicherweise können Sie die vertrauliche Datei unabhängig von ihrem Besitz und ihrer Erlaubnis lesen, wenn Sie die Testschritte in genau derselben Reihenfolge erneut ausführen. Dieses Problem in opcache wurde schon lange gemeldet, aber zum Zeitpunkt dieses Artikels wurde es noch nicht behoben.

Fazit

Aus Sicherheitsgründen ist es wichtig, PHP-FPM-Pools mit einem anderen Benutzer für jede Site auf demselben Nginx-Webserver zu verwenden. Selbst wenn dies mit einer kleinen Leistungsstrafe verbunden ist, kann der Vorteil einer solchen Isolation schwerwiegende Sicherheitsverletzungen verhindern.

Die in diesem Artikel beschriebene Idee ist nicht einzigartig und auch in anderen ähnlichen PHP-Isolationstechnologien wie SuPHP vorhanden. Die Leistung aller anderen Alternativen ist jedoch viel schlechter als die von php-fpm.

Related