Ubuntu Server 14.04 – phpMyAdmin installieren und absichern

phpMyAdmin ist ein in der Skriptsprache PHP geschriebenes Werkzeug zur Administration von MySQL– und MariaDB-Datenbankservern. Die webbasierte Benutzeroberfläche ermöglicht die Datenbankverwaltung ohne detaillierte Kenntnisse des SQL-Befehlssatzes. phpMyAdmin setzt einen Webserver mit PHP-Unterstützung voraus, der z. B. im Softwarepaket LAMP enthalten ist.

Nachfolgend werden wir phpMyAdmin unter Ubuntu Server 14.04 mit installiertem LAMP-Server einrichten und mit verschiedenen Maßnahmen wie ausschließlicher HTTPS-Übertragung und HTTP-Authentifizierung absichern. Abschließend werden wir eine spezielle phpMyAdmin-Subdomain anlegen, die nur über einen Nicht-Standard-HTTPS-Port erreichbar ist. Darüber hinausgehende empfehlenswerte Sicherheitsvorkehrungen sind Gegenstand des Artikels Ubuntu Server 14.04 – Webserver Apache absichern.

Voraussetzungen

Wir haben unseren Server z. B. gemäß den Anleitungen zur Ubuntu-Server-Basisinstallation und zur LAMP-Server-Installation 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.

phpMyAdmin installieren

Die phpMyAdmin-Installation erfolgt unter Ubuntu Server 14.04 am einfachsten mit dem apt-Befehl:

root@server:~# apt install phpmyadmin

Während des Installationsvorgangs wird eine Auswahl von Webservern angezeigt, die auf Wunsch automatisch konfiguriert werden können. Hier kreuzen wir apache2 an ([*]). Die Abfrage, ob die Datenbank für phpMyAdmin mit dbconfig-common konfiguriert werden soll, beantworten wir mit Nein, da wir die Einrichtung manuell vornehmen werden.

Zur Sicherheit gestatten wir nur dem Eigentümer root Vollzugriff auf den Standard-Installationsordner /usr/share/phpmyadmin und erlauben der Apache-Gruppe www-data ausschließlich lesenden Zugriff:

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

Bei der automatischen Webserver-Konfiguration wird die Konfigurationsdatei /etc/phpmyadmin/apache.conf erzeugt, unter /etc/apache2/conf-available/phpmyadmin.conf ein Link auf diese Datei angelegt und die Konfiguration in Apache aktiviert. Ist der externe Zugriff auf das Verzeichnis /usr/share, wie im Artikel zur LAMP-Server-Installation beschrieben, aus Sicherheitsgründen gesperrt, müssen wir den Unterordner phpmyadmin durch folgenden zusätzlichen Eintrag in apache.conf freigeben:

...
<Directory /usr/share/phpmyadmin>
  ...
  Require all granted
  ...
</Directory>
...

Nach einem Neustart von Apache mit

root@server:~# service apache2 restart

können wir phpMyAdmin zum ersten Mal im Browser aufrufen:

http://www.test.local/phpmyadmin

Nach der Anmeldung mit Benutzername und Passwort für den MySQL-Server erhalten wir die Fehlermeldung:

Die Erweiterung mcrypt fehlt. Bitte die PHP-Konfiguration überprüfen.

Das fehlende PHP-Modul Mcrypt aktivieren wir mit php5enmod und starten danach Apache neu:

root@server:~# php5enmod mcrypt
root@server:~# service apache2 restart

Zuätzlich wird von phpMyAdmin folgende Meldung angezeigt:

Der phpMyAdmin-Konfigurationsspeicher ist nicht vollständig konfiguriert, einige erweiterte Funktionen wurden deaktiviert. Klicken Sie hier, um herauszufinden warum.

Nach Anklicken des Links erhält man eine Liste der Zusatzfunktionen, die mit dem phpMyAdmin-Konfigurationsspeicher genutzt werden können, und eine Kurzanleitung zur schrittweisen Einrichtung des Speichers.

Zunächst erstellen wir eine zentrale Datenbank phpmyadmin mit den Tabellen für den Konfigurationsspeicher. Im zugehörigen Abschnitt über den phpMyAdmin configuration storage in der Originaldokumentation wird auf eine SQL-Skriptdatei create_tables.sql verwiesen, die diesen Vorgang automatisiert. Diese Skriptdatei befindet sich bei Ubuntu 14.04 allerdings nicht im Unterordner sql des Installationsverzeichnisses /usr/share/phpmyadmin, sondern im Verzeichnis /usr/share/doc/phpmyadmin/examples. Da die Datei in gzip-komprimierter Form vorliegt, müssen wir sie vor der Nutzung entpacken:

root@server:~# gunzip /usr/share/doc/phpmyadmin/examples/create_tables.sql.gz

Zur Ausführung des Skripts starten wir den MySQL-Client als Benutzer root und laden create_tables.sql mit der Anweisung source:

root@server:~# mysql -p -u root
Enter password: ***
...
mysql> source /usr/share/doc/phpmyadmin/examples/create_tables.sql
...

Danach legen wir einen speziellen Benutzer pma, den sogenannten „Control User“, mit passenden Zugriffsrechten auf die Datenbank phpmyadmin an und beenden den MySQL-Client:

mysql> GRANT SELECT, INSERT, DELETE, UPDATE ON phpmyadmin.* TO 'pma'@'localhost' IDENTIFIED BY '***';
mysql> exit

Abschließend aktivieren wir die gewünschten Zusatzfunktionen durch Anpassen der zugehörigen Parameter in der Konfigurationsdatei config.inc.php. Diese Datei liegt bei Ubuntu 14.04 im Verzeichnis /etc/phpmyadmin. Die Anpassungen nehmen wir im Abschnitt Storage database and tables vor, in dem bereits auskommentierte Mustereinträge hinterlegt sind. In der Variablen $cfg['Servers'][$i]['pmadb'] speichern wir den Namen der Konfigurationsspeicher-Datenbank phpmyadmin. Die übrigen Einträge $cfg['Servers'][$i]['…'] beinhalten die für die erweiterten Funktionen benötigten Tabellen. Zur Aktivierung entfernen wir die Kommentarzeichen // und ergänzen noch einen Unterstrich hinter dem Präfix pma, da die Namen der mit dem Skript create_tables.sql angelegten Tabellen an dieser Stelle nur einen statt zwei Unterstriche enthalten. Wollen wir den vollen erweiterten Funktionsumfang nutzen, sind folgende Änderungen in config.inc.php erforderlich:

...
/* Storage database and tables */
// $cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
$cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
// $cfg['Servers'][$i]['bookmarktable'] = 'pma_bookmark';
$cfg['Servers'][$i]['bookmarktable'] = 'pma__bookmark';
// $cfg['Servers'][$i]['relation'] = 'pma_relation';
$cfg['Servers'][$i]['relation'] = 'pma__relation';
// $cfg['Servers'][$i]['table_info'] = 'pma_table_info';
$cfg['Servers'][$i]['table_info'] = 'pma__table_info';
// $cfg['Servers'][$i]['table_coords'] = 'pma_table_coords';
$cfg['Servers'][$i]['table_coords'] = 'pma__table_coords';
// $cfg['Servers'][$i]['pdf_pages'] = 'pma_pdf_pages';
$cfg['Servers'][$i]['pdf_pages'] = 'pma__pdf_pages';
// $cfg['Servers'][$i]['column_info'] = 'pma_column_info';
$cfg['Servers'][$i]['column_info'] = 'pma__column_info';
// $cfg['Servers'][$i]['history'] = 'pma_history';
$cfg['Servers'][$i]['history'] = 'pma__history';
// $cfg['Servers'][$i]['table_uiprefs'] = 'pma_table_uiprefs';
$cfg['Servers'][$i]['table_uiprefs'] = 'pma__table_uiprefs';
// $cfg['Servers'][$i]['tracking'] = 'pma_tracking';
$cfg['Servers'][$i]['tracking'] = 'pma__tracking';
// $cfg['Servers'][$i]['designer_coords'] = 'pma_designer_coords';
$cfg['Servers'][$i]['designer_coords'] = 'pma__designer_coords';
// $cfg['Servers'][$i]['userconfig'] = 'pma_userconfig';
$cfg['Servers'][$i]['userconfig'] = 'pma__userconfig';
// $cfg['Servers'][$i]['recent'] = 'pma_recent';
$cfg['Servers'][$i]['recent'] = 'pma__recent';
...

Damit die neue Konfiguration wirksam wird, melden wir uns schließlich noch in phpMyAdmin ab und wieder an.

phpMyAdmin absichern

Zur Absicherung von phpMyAdmin folgen wir den Empfehlungen aus dem Abschnitt Securing your phpMyAdmin installation der Originaldokumentation.

Die Empfehlung, die phpMyAdmin-Website nur per „HyperText Transfer Protocol Secure“ HTTPS zur Verfügung zu stellen und falls möglich „HTTP Strict Transport Security“ HSTS zu verwenden, können wir in Anlehnung an die Beschreibung zur LAMP-Server-Installation und den Artikel über die Absicherung des Webservers Apache umsetzen. Aus Gründen der Übersichtlichkeit gehen wir an dieser Stelle nicht weiter auf Details ein. Die veraltete Option $cfg['ForceSSL'] sollte man gemäß der phpMyAdmin-Dokumentation zu ForceSSL stattdessen nicht mehr nutzen.

Da wir Konfigurationsänderungen in der Datei config.inc.php direkt vornehmen, benötigen wir das Unterverzeichnis setup nicht und können es wie empfohlen löschen:

root@server:~# rm -r /usr/share/phpmyadmin/setup

Dadurch werden auch die zugehörigen Directory-Blöcke in der Apache-Konfigurationsdatei /etc/phpmyadmin/apache.conf überflüssig und können entfernt / auskommentiert werden:

...
#<Directory /usr/share/phpmyadmin/setup>
#  <IfModule mod_authn_file.c>
#  AuthType Basic
#  AuthName "phpMyAdmin Setup"
#  AuthUserFile /etc/phpmyadmin/htpasswd.setup
#  </IfModule>
#  Require valid-user
#</Directory>
...
#<Directory /usr/share/phpmyadmin/setup/lib>
#  Order Deny,Allow
#  Deny from All
#</Directory>

Bei dieser Gelegenheit können wir auch gleich die veraltete Syntax (siehe Apache-Dokumentation Upgrading to 2.4 from 2.2) im Directory-Block /usr/share/phpmyadmin/libraries ersetzen:

...
<Directory /usr/share/phpmyadmin/libraries>
#  Order Deny,Allow
#  Deny from All
  Require all denied
</Directory>
...

Die Änderungen werden nach einem Neustart von Apache übernommen:

root@server:~# service apache2 restart

Da die empfohlene Cookie-Authentifizierung mit Abfrage des MySQL-Benutzernamens und des Passworts voreingestellt ist, müssen wir an der Authentifizierungsmethode nichts ändern.

Falls Zugriffsbeschränkungen für Benutzer und / oder IP-Adressen gewünscht werden, kann man diese mit dem Parameter $cfg['Servers'][$i]['AllowDeny']['rules'] in Kombination mit $cfg['Servers'][$i]['AllowDeny']['order'] realisieren. Die Beschränkungen sind speziell auf den jeweiligen Anwendungsfall abzustimmen, sodass wir hier kein allgemeines Konfigurationsbeispiel angeben.

Zur weiteren Absicherung können wir eine HTTP-Basisauthentifizierung (siehe Apache-How-To Authentication and Authorization) einrichten, die der Cookie-Authentifizierung vorgeschaltet ist. Hierzu ergänzen wir den Directory-Block /usr/share/phpmyadmin in /etc/phpmyadmin/apache.conf:

...
<Directory /usr/share/phpmyadmin>
  ...
  #Require all granted
  AuthBasicProvider file
  AuthName "phpmyadmin"
  AuthType Basic
  AuthUserFile /etc/apache2/.htpasswd_phpmyadmin
  Require valid-user
  ...
</Directory>
...

Nach dem Speichern der Änderungen starten wir den Apache-Webserver neu:

root@server:~# service apache2 restart

Die Anweisungen für die Basisauthentifizierung sind auf die vorinstallierten Apache-Module mod_auth_basic, mod_authn_core, mod_authn_file und mod_authz_core verteilt. Mit AuthType legen wir den Authentifizierungstyp, hier Basic, fest. AuthBasicProvider file bewirkt, dass zur Authentifizierung auf die in AuthUserFile angegebene Benutzerdatei /etc/apache2/.htpasswd_phpmyadmin zurückgegriffen wird. Durch die Anweisung Require valid-user statt des auszukommentierenden Require all granted erhalten nur die in der Benutzerdatei zusammen mit ihren Passwörtern aufgeführten Benutzer Zugriff auf phpMyAdmin. In AuthName ist schließlich der Name phpmyadmin hinterlegt, der bei der Benutzer-/Passwortabfrage des Browsers angezeigt wird.

Zum Anlegen und Verwalten der Benutzerdatei .htpasswd_phpmyadmin können wir das Programm htpasswd aus der Sammlung mit Webserver-Hilfsprogrammen apache2-utils verwenden, die wir vorab noch installieren:

root@server:~# apt install apache2-utils
root@server:~# htpasswd -B -c /etc/apache2/.htpasswd_phpmyadmin theo
New password: ***
Re-type new password: ***

Der obige Aufruf von htpasswd erzeugt (Option -c) eine Textdatei /etc/apache2/.htpasswd_phpmyadmin mit einem Eintrag für den Benutzer theo und dem eingegebenen Passwort in verschlüsselter Form. Zur Passwortverschlüsselung kommt als sicherste der von htpasswd angebotenen Methoden bcrypt (Option -B) zum Einsatz. Weitere Benutzer können durch Programmaufrufe ohne die Option -c angelegt werden. Die Wahl der Benutzernamen und Passwörter ist dabei völlig unabhängig von der Benutzerverwaltung des Betriebssystems.

„Security by Obscurity“ einrichten

Durch die folgenden zusätzlichen „Security by Obscurity“-Maßnahmen können wir böswillige Attacken auf unsere phpMyAdmin-Installation erschweren.

Wie ein Auszug aus der Apache-Logdatei eines Servers im produktiven Betrieb zeigt, versuchen Angreifer systematisch Zugriff auf das phpMyAdmin-Installationsverzeichnis zu erlangen, wobei sie neben dem Standardordner phpmyadmin auch ähnliche Ordnernamen durchprobieren:

...
[...] [authz_core:error] [pid ...] [client ...] AH01630: client denied by server configuration: /var/www/phpMyAdmin
[...] [authz_core:error] [pid ...] [client ...] AH01630: client denied by server configuration: /var/www/phpmyadmin
[...] [authz_core:error] [pid ...] [client ...] AH01630: client denied by server configuration: /var/www/pma
[...] [authz_core:error] [pid ...] [client ...] AH01630: client denied by server configuration: /var/www/myadmin
[...] [authz_core:error] [pid ...] [client ...] AH01630: client denied by server configuration: /var/www/MyAdmin
...

Um den unerwünschten Zugang schwieriger zu gestalten, nennen wir das Unterverzeichnis phpmyadmin um, vorzugsweise in eine zufällige Zeichenfolge, die wir z. B. mit einem Passwortgenerator erzeugen. In der Apache-Konfigurationsdatei /etc/phpmyadmin/apache.conf ist dazu der Alias-Eintrag zu ändern. In unserem Beispiel wählen wir einen Namen, der aus einer Kombination aus 20 Großbuchstaben, Kleinbuchstaben und Ziffern besteht:

...
#Alias /phpmyadmin /usr/share/phpmyadmin
Alias /lRxm5RDkHRy2O7riVue9 /usr/share/phpmyadmin
...

Nach dem obligatorischen Neustart von Apache mit

root@server:~# service apache2 restart

ist phpMyAdmin nun unter

https://www.test.local/lRxm5RDkHRy2O7riVue9

zu erreichen.

Eine weitergehende „Security by Obscurity“-Maßnahme ist das Einrichten eines zusätzlichen virtuellen Apache-Hosts für eine eigene phpMyAdmin-Subdomain, die nur über einen vom HTTPS-Standard-Port 443 abweichenden Port wie z. B. 42443 ansprechbar ist. Hierzu erstellen wir in Analogie zur Datei default-ssl.conf aus dem Artikel zur LAMP-Server-Installation eine HTTPS-Konfigurationsdatei /etc/apache2/sites-available/lRxm5RDkHRy2O7riVue9.conf und ergänzen diese um Einträge aus /etc/phpmyadmin/apache.conf:

<VirtualHost *:42443>
  CustomLog ${APACHE_LOG_DIR}/access-phpmyadmin.log vhost_combined
  DocumentRoot /usr/share/phpmyadmin
  ErrorLog ${APACHE_LOG_DIR}/error-phpmyadmin.log
  Header always set Strict-Transport-Security "max-age=31536000"
  ServerAdmin webmaster@test.local
  ServerName lRxm5RDkHRy2O7riVue9.test.local
  SSLCertificateFile /etc/ssl/certs/lRxm5RDkHRy2O7riVue9.test.local.pem
  SSLCertificateKeyFile /etc/ssl/private/lRxm5RDkHRy2O7riVue9.test.local.key
  SSLEngine on
  <Directory /usr/share/phpmyadmin>
    AuthBasicProvider file
    AuthName "phpmyadmin"
    AuthType Basic
    AuthUserFile /etc/apache2/.htpasswd_phpmyadmin
    DirectoryIndex index.php
    Options FollowSymLinks
    Require valid-user
    <IfModule mod_php5.c>
      AddType application/x-httpd-php .php
      php_flag magic_quotes_gpc Off
      php_flag track_vars On
      php_flag register_globals Off
      php_admin_flag allow_url_fopen Off
      php_value include_path .
      php_admin_value upload_tmp_dir /var/lib/phpmyadmin/tmp
      php_admin_value open_basedir /usr/share/phpmyadmin/:/etc/phpmyadmin/:/var/lib/phpmyadmin/:/usr/share/php/php-gettext/:/usr/share/javascript/
    </IfModule>
  </Directory>
  <Directory /usr/share/phpmyadmin/libraries>
    Require all denied
  </Directory>
</VirtualHost>

Danach aktivieren wir die Konfiguration mit a2ensite und deaktivieren mit a2disconf den Link /etc/apache2/conf-available/phpmyadmin.conf auf die ursprüngliche Konfigurationsdatei /etc/phpmyadmin/apache.conf:

root@server:~# a2ensite lRxm5RDkHRy2O7riVue9.conf
root@server:~# a2disconf phpmyadmin.conf

Die Zertifikatsdateien lRxm5RDkHRy2O7riVue9.test.local.pem und lRxm5RDkHRy2O7riVue9.test.local.key kann man für ein lokales Netzwerk nach der Anleitung im Artikel Certificate Authority mit OpenSSL einrichten erzeugen. Die Vorgehensweise zur Generierung von Zertifikaten für den öffentlichen Internetzugang beschreibt der Beitrag SSL-Zertifikate mit Let’s Encrypt. Wir nutzen hier das Skript genservcert.sh aus erstgenanntem Artikel:

root@server:~# /root/genservcert.sh lRxm5RDkHRy2O7riVue9.test.local "Theo Test" ca_test.local
...
Enter pass phrase for /etc/ssl/private/ca_test.local.key: ***

Damit Apache den Port 42443 auch abfragt, fügen wir in der Port-Konfigurationsdatei /etc/apache2/ports.conf noch zwei Einträge Listen 42443 hinzu:

...
<IfModule ssl_module>
  Listen 443
  Listen 42443
</IfModule>
<IfModule mod_gnutls.c>
  Listen 443
  Listen 42443
</IfModule>
...

Schließlich starten wir Apache neu:

root@server:~# service apache2 restart

Nach eventuell für die neue Subdomain lRxm5RDkHRy2O7riVue9.test.local erforderlichen Anpassungsarbeiten an dem / den DNS-Server/n können wir die phpMyAdmin-Benutzeroberfläche mit

https://lRxm5RDkHRy2O7riVue9.test.local:42443

im Browser aufrufen.