Ubuntu Server – PHP absichern

Für den Betrieb von Websites wird häufig der Webserver Apache in Kombination mit der Skriptsprache PHP eingesetzt. Nach der Standardinstallation, z. B. zusammen mit dem Softwarepaket LAMP, enthält die PHP-Umgebung allerdings einige mehr oder weniger sicherheitskritische Voreinstellungen.
Zur Absicherung von PHP werden wir in diesem Beitrag sicherheitsrelevante Direktiven in der zentralen Konfigurationsdatei php.ini anpassen. Wir folgen hierbei den Empfehlungen des Analyseprogramms PhpSecInfo, das wir vorab auf unserem Testsystem mit Ubuntu Server 14.04 installieren. Die von PhpSecInfo vorgeschlagenen Änderungen können ein sicherheitsbewusstes Programmdesign natürlich nicht ersetzen, stellen aber eine sinnvolle Ergänzung dar.
Die beschriebenen Maßnahmen sind recht restriktiv und müssen sicherlich in vielen Fällen, insbesondere bei nicht selbst programmierten Webanwendungen, aufgeweicht werden. Hier hilft oft nur ein kompletter Funktionstest und / oder eine Analyse des Programmcodes.

Voraussetzungen

Wir haben unseren Server z. B. gemäß den Anleitungen zur Ubuntu-Server-Basisinstallation und zur LAMP-Server-Installation installiert und konfiguriert und durch Updates / Upgrades auf den neuesten Stand gebracht. Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

PhpSecInfo installieren

Zur Installation von PhpSecInfo benötigen wir die Versionsverwaltung Git. Falls Git noch nicht auf unserem Server läuft, müssen wir das Programm zunächst z. B. mit apt installieren:

root@server:~# apt install git

Danach kopieren wir mit git clone die Paketquellen in das (frei wählbare) Verzeichnis /usr/share/phpsecinfo, das dabei automatisch angelegt wird:

root@server:~# git clone https://github.com/bigdeej/PhpSecInfo /usr/share/phpsecinfo

Bei einer eventuellen zukünftigen Aktualisierung auf eine neue Programmversion können Updates mit git pull eingespielt werden:

root@server:~# git -C /usr/share/phpsecinfo pull

Zur Sicherheit erhält nur der Benutzer root Vollzugriff auf den Ordner /usr/share/phpsecinfo. Den Mitgliedern der Gruppe www-data, also auch dem Apache-Serverdienst, gestatten wir nur lesenden Zugriff:

root@server:~# chown -R root:www-data /usr/share/phpsecinfo
root@server:~# find /usr/share/phpsecinfo \( -type d -exec chmod 0750 {} + \) -o \( -type f -exec chmod 0640 {} + \)

Damit die Testergebnisse von PhpSecInfo und somit eventuelle Sicherheitslücken unserer PHP-Installation nicht öffentlich zugänglich sind, erstellen wir eine separate, ausschließlich per HTTPS erreichbare Subdomain phpsecinfo.test.local, die durch eine HTTP-Basisauthentifizierung geschützt ist. Zusätzlich nutzen wir die in Ubuntu Server 14.04 – Webserver Apache absichern behandelten Sicherheitsheader.
Die Virtual-Host-Konfiguration für Apache orientiert sich am Beitrag über die sichere Installation von phpMyAdmin. Auf „Security by Obscurity“-Maßnahmen wie einen geänderten HTTPS-Port oder einen zufallsgenerierten Namen für die Subdomain verzichten wir der Übersichtlichkeit halber. Da PhpSecInfo Inline-CSS verwendet, ist ein zusätzlicher Eintrag style-src ’self‘ ‚unsafe-inline‘ in der Content Security Policy nötig. Die resultierende Konfigurationsdatei /etc/apache2/sites-available/phpsecinfo.conf lautet:

<VirtualHost *:443>
  CustomLog ${APACHE_LOG_DIR}/access-phpsecinfo.log vhost_combined
  DocumentRoot /usr/share/phpsecinfo
  ErrorLog ${APACHE_LOG_DIR}/error-phpsecinfo.log
  Header always set Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline'"
  Header always set Strict-Transport-Security "max-age=31536000"
  Header always set X-Content-Type-Options "nosniff"
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set X-Xss-Protection "1; mode=block"
  ServerAdmin webmaster@test.local
  ServerName phpsecinfo.test.local
  SSLCertificateFile /etc/ssl/certs/phpsecinfo.test.local.pem
  SSLCertificateKeyFile /etc/ssl/private/phpsecinfo.test.local.key
  SSLEngine on
  <Directory /usr/share/phpsecinfo>
    AuthBasicProvider file
    AuthName "phpsecinfo"
    AuthType Basic
    AuthUserFile /etc/apache2/.htpasswd_phpsecinfo
    Require valid-user
  </Directory>
</VirtualHost>

Abschließend aktivieren wir die PhpSecInfo-Konfiguration mit a2ensite und starten Apache neu:

root@server:~# a2ensite phpsecinfo.conf
root@server:~# service apache2 restart

Um die neue Subdomain ansprechen zu können, sind eventuell noch systemspezifische Anpassungen an dem / den DNS-Server/n erforderlich, auf die wir hier nicht weiter eingehen.

PHP-Umgebung mit PhpSecInfo analysieren und anpassen

Zur Analyse unserer PHP-Installation rufen wir PhpSecInfo im Browser auf:

https://phpsecinfo.test.local

Auf unserem Testsystem mit Ubuntu Server 14.04 und PHP 5.5.9 wurden insgesamt 39 Warnungen und 6 Bemerkungen zu möglichen Sicherheitsrisiken in den Bereichen Core, Session, Dir und Functions ausgegeben:

...
Test Results Summary
Test    Result
Pass    22 out of 67 (32.84%)
Notice  6 out of 67 (8.96%)
Warning 39 out of 67 (58.21%)
Score   37.31%

Details zu den Einträgen liefert ein Klick auf den jeweiligen Verweis More information. Im Bereich Functions wird leider statt einer Erläuterung nur eine Not Found-Fehlermeldung ausgegeben. Informationen über die einzelnen Funktionen kann man stattdessen im PHP-Handbuch nachschlagen.
Die aufgeführten Empfehlungen können wir durch Ändern von Direktiven in der zentralen Konfigurationsdatei /etc/php5/apache2/php.ini umsetzen. Da bei mehrfacher Verwendung derselben Direktive das letzte Auftreten ausgewertet wird, setzen wir die Anweisungen gebündelt an das Dateiende und ersparen uns so nebenbei das Auskommentieren der Originaleinträge:

...
upload_tmp_dir = /xxx/
open_basedir = /xxx/
file_uploads = Off
allow_url_fopen = Off
disable_functions = ${disable_functions}posix_setpgid,exec,ftp_login,mysql_pconnect,apache_setenv,popen,posix_getpwuid,posix_setsid,passthru,escapeshellcmd,ini_alter,ftp_raw,ftp_nb_fput,ini_restore,shell_exec,ftp_get,proc_get_status,highlight_file,proc_close,proc_terminate,syslog,ftp_connect,posix_uname,ini_get_all,proc_open,posix_kill,escapeshellarg,ftp_rawlist,posix_setuid,openlog,php_uname,system,ftp_exec,posix_mkfifo,proc_nice,ftp_put

Im Verzeichnis upload_tmp_dir werden Uploads zwischengespeichert, bevor sie von einem PHP-Skript weiterverarbeitet werden. open_basedir schränkt den PHP-Dateizugriff auf den angegebenen Ordner ein. Für beide Verzeichnisse wählen wir den nicht-existierenden Ordnernamen /xxx/ und erzwingen damit, dass bei Bedarf in einer Apache-Website-Konfiguration passende Verzeichnisse explizit angegeben werden müssen.
Per Voreinstellung erlaubt PHP das Hochladen von Dateien, in der Regel in einem Formular im Webbrowser. file_uploads = Off unterbindet diese Möglichkeit und verringert so die Angriffsfläche für bösartige Attacken.
Mit den Dateifunktionen von PHP können auch externe Daten, beispielsweise von anderen Websites, gelesen werden. Die Anweisung allow_url_fopen = Off beschränkt den Zugriff auf lokale Dateien und verhindert das sonst auf diesem Weg mögliche Einschleusen von Schadcode.
Funktionen, die zum Sprachumfang von PHP gehören, aber z. B. aus Sicherheitsgründen nicht bereitgestellt werden sollen, werden mit der Direktive disable_functions deaktiviert. Gemäß den Ausführungen in der PHP-Dokumentation zur Prozesskontrolle PCNTL sind die PCNTL-Funktionen mit dem Präfix pcntl hier bereits per Komma getrennt aufgelistet. Mit ${disable_functions} greifen wir auf diese ursprüngliche Liste zu und ergänzen sie um die Vorschläge von PhpSecInfo.
Neben der zentralen Einstellung in php.ini können Direktiven auch in Benutzerskripten, in .htaccess-Dateien, in Apache-Konfigurationsdateien und – nur für CGI/FastCGI-SAPI – in der .user.ini eingesetzt werden. Einen Überblick gibt die Liste der php.ini-Direktiven der PHP-Dokumentation.
Durch die Anweisung open_basedir = /xxx/ haben wir uns auch den Zugang zu PhpSecInfo versperrt. Mit einem zusätzlichen php_admin_value-Eintrag in der Apache-Konfigurationsdatei phpsecinfo.conf geben wir das Verzeichnis /usr/share/phpsecinfo/ wieder für PHP frei:

...
<Directory /usr/share/phpsecinfo>
  ...
  php_admin_value open_basedir /usr/share/phpsecinfo/
</Directory>

Zur Übernahme der Änderungen ist ein Neustart von Apache erforderlich:

root@server:~# service apache2 restart

Beim zweiten Testlauf mit der modifizierten php.ini-Datei gibt es nur noch einen Hinweis auf die veraltete PHP-Version 5.5.9. Da mit dem LAMP-Server unter Ubuntu 14.04 keine aktuellere Version installiert wird, können wir dies leider nicht bzw. nur mit einer manuellen Installation von PHP ändern.
Die Core-Sicherheitswarnungen zum Benutzer / zur Gruppe www-data des Apache-Servers mit einer (angeblich) privilegierten user_id / group_id verschwinden mit der Deaktivierung der Funktion exec in disable_functions. Da www-data neben anderen wichtigen Benutzerkonten und Gruppen auf jedem Ubuntu-System eine einheitliche user_id und group_id von 33 zugeordnet wird, sollten wir diese allerdings selbst bei Nutzung von exec nicht ändern.
Die Bemerkungen zu save_path und is_root_read in den Bereichen Session und Dir werden wegen der Änderung der Einstellung in open_basedir nicht mehr angezeigt.