Setting up and securing a phpMyAdmin install on Ubuntu 10.04

Last time out we looked at setting up a LAMP server ready to start developing dynamic websites, without really going into much detail beyond the absolute basics. Day to day web development on a LAMP stack by and large ignores the LA and concentrates on the MP - so let's have a look at getting one of the more common ways of accessing MySQL set up on a newly prepared server. Note that the rest of this post assumes you have followed the instructions found in the LAMP server setup post.

Installing phpMyAdmin

Getting phpMyAdmin installed is easy:

sudo apt-get install phpmyadmin

This will ask you to install quite a few dependent packages - go ahead and hit enter when prompted. The next screen you'll see will be a package configuration prompt - go ahead and select the option to configure phpMyAdmin for Apache2 automatically (use space to select the option and then the arrow keys / tab to navigate to Ok). You'll be returned to your terminal briefly before being presented with another configuration screen.

As per the suggestion in the prompt, you should choose the default option which is to let the installation process configure phpMyAdmin's own database, which upon selection will prompt you for your database's administrative user (aka root - though not your server's root account). Note that this is a password you chose during setup, not a new one you can just make up. The next prompt, however, is a password for the (newly created) phpmyadmin user to access its own database - simply enter a blank password and one will be generated randomly. You should never need to login as this user, so it's not a problem to not know what its password is.

Accessing phpMyAdmin

Assuming the installation process above went smoothly, you can now access your phpMyAdmin instance at:

http://<your-server-ip-address>/phpmyadmin

Great stuff - you're all set up to go. But if you're tempted to leave it there, don't. Let's tighten up access a bit - after all, you've suddenly exposed an easy access point to what will become a critically important part of every site you host on this server. So what's the problem? Well, there are two big ones.

Problem #1

First of all, let's look at how the default configuration is exposing /phpmyadmin to try and get a better understanding of the out-of-the-box setup:

nick@pd01:~$ ls -lah /etc/apache2/sites-enabled
total 8.0K
drwxr-xr-x 2 root root 4.0K 2011-09-06 09:35 .
drwxr-xr-x 7 root root 4.0K 2011-09-06 09:35 ..
lrwxrwxrwx 1 root root 26 2011-09-06 09:35 000-default -> ../sites-available/default

Hmm. Nothing to see there - but that makes sense, since we didn't access it as a VirtualHost. Let's try apache's conf.d directory:

nick@pd01:~$ ls -lah /etc/apache2/conf.d/
total 20K
drwxr-xr-x 2 root root 4.0K 2011-09-06 09:54 .
drwxr-xr-x 7 root root 4.0K 2011-09-06 09:35 ..
-rw-r--r-- 1 root root 269 2010-04-13 20:20 charset
lrwxrwxrwx 1 root root 45 2011-09-06 09:43 javascript-common.conf -> /etc/javascript-common/javascript-common.conf 
-rw-r--r-- 1 root root 3.3K 2010-04-13 20:20 localized-error-pages 
lrwxrwxrwx 1 root root 28 2011-09-06 09:54 phpmyadmin.conf -> ../../phpmyadmin/apache.conf 
-rw-r--r-- 1 root root 1.5K 2010-04-13 20:20 security

Ah ha - so we've got a phpmyadmin.conf file which is simply a symlink to /etc/phpmyadmin/apache.conf. That makes sense. Let's take a look at the first few lines of it:

# phpMyAdmin default Apache configuration
Alias /phpmyadmin /usr/share/phpmyadmin
<Directory /usr/share/phpmyadmin>
    Options FollowSymLinks
    DirectoryIndex index.php
    <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_value include_path .
    </IfModule>
</Directory>

That explains it then - we've simply got a standard Apache Alias pointing every request starting with /phpmyadmin to the phpMyAdmin installation directory.

So what?

The problem is that the Alias directive is applied globally. Assuming you're going to add multiple VirtualHosts to this server, every single one of them will expose a route to /phpmyadmin. If you set up a domain as per the earlier post, try accessing /phpmyadmin on it now. It'll work. Not good.

Problem #2

Not only are we exposing the URL to phpMyAdmin with reckless abandon (check your server logs and I guarantee you you'll find plenty of bots trying to access it after a while), but we're exposing it over http, meaning all traffic is unencrypted. The problem here should be fairly obvious - this is after all your database which presumably will soon be full of lots of important, potentially sensitive and valuable data - data you may be obliged to keep private and almost certainly obliged to keep secure. We need to get communication running over SSL, and we need to stop exposing the default /phpmyadmin path.

A solution

A solution - not necessarily the solution, is fairly simple:

  1. Get rid of the global Alias directive
  2. Set up a new VirtualHost to serve phpMyAdmin from
  3. Only expose this VirtualHost over SSL

Setting up the VirtualHost

Let's move the phpmyadmin.conf file into /etc/apache2/sites-available and set about getting rid of that Alias and wrapping the whole thing in a VirtualHost:

nick@pd01:~$ sudo mv /etc/apache2/conf.d/phpmyadmin.conf /etc/apache2/sites-available/

Now edit the file with your favourite editor (use sudo or you won't be able to write your changes) and change it to look like the following:

<VirtualHost *:80>
    ServerName my-fake-domain
    DocumentRoot /usr/share/phpmyadmin
    <Directory /usr/share/phpmyadmin>
        Options FollowSymLinks
        DirectoryIndex index.php
        <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_value include_path . 
        </IfModule> 
    </Directory> 
    # Authorize for setup 
    <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> 
    # Disallow web access to directories that don't need it 
    <Directory /usr/share/phpmyadmin/libraries> 
        Order Deny,Allow 
        Deny from All 
    </Directory> 
    <Directory /usr/share/phpmyadmin/setup/lib> 
        Order Deny,Allow 
        Deny from All 
    </Directory> 
</VirtualHost>

All we've done here is removed the Alias line, added two familiar VirtualHost specific directives (ServerName and DocumentRoot), and wrapped the whole thing in a VirtualHost declaration. I've chosen a ServerName which doesn't actually resolve, so I'd have to add my-fake-domain to the /etc/hosts file of each computer I wanted to access phpMyAdmin from. You don't need this to be a real domain, so you might as well choose a fake one to just reduce your attack surface that little bit further.

If you restart Apache now and try and access the /phpmyadmin path you'll now get Apache's default 404 page but won't be able to access your new VirtualHost (we haven't enabled it yet) - so let's finish off our configuration.

Setting up SSL

Assuming you've got mod_ssl enabled you should have a VirtualHost template which we're going to pinch the relevant bits from for this next step. Take a peak at /etc/apache2/sites-available/default-ssl - it's quite large so we won't duplicate it here, but it's worth having a quick read over. We're only interested in a few lines:

	#   SSL Engine Switch:
	#   Enable/Disable SSL for this virtual host.
	SSLEngine on

	#   A self-signed (snakeoil) certificate can be created by installing
	#   the ssl-cert package. See
	#   /usr/share/doc/apache2.2-common/README.Debian.gz for more info.
	#   If both key and certificate are stored in the same file, only the
	#   SSLCertificateFile directive is needed.
	SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
	SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

Let's add these lines to our phpmyadmin.conf file - be sure to change the port the VirtualHost is bound to from 80 to 443. We're also going to add a quick RewriteRule to block requests with an incorrect http host header (more on that later):


<VirtualHost *:443>
	ServerName my-fake-domain
	DocumentRoot /usr/share/phpmyadmin

        RewriteEngine On
        RewriteCond %{HTTP_HOST} !my-fake-domain
        RewriteRule (.*) [L]

	<Directory /usr/share/phpmyadmin>
		Options FollowSymLinks
		DirectoryIndex index.php

		<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_value include_path .
		</IfModule>

	</Directory>

	# Authorize for setup
	<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>

	# Disallow web access to directories that don't need it
	<Directory /usr/share/phpmyadmin/libraries>
	    Order Deny,Allow
	    Deny from All
	</Directory>
	<Directory /usr/share/phpmyadmin/setup/lib>
	    Order Deny,Allow
	    Deny from All
	</Directory>

	#   SSL Engine Switch:
	#   Enable/Disable SSL for this virtual host.
	SSLEngine on

	#   A self-signed (snakeoil) certificate can be created by installing
	#   the ssl-cert package. See
	#   /usr/share/doc/apache2.2-common/README.Debian.gz for more info.
	#   If both key and certificate are stored in the same file, only the
	#   SSLCertificateFile directive is needed.
	SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
	SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
</VirtualHost>

Now simply restart apache, navigate to https://my-fake-domain, choose to ignore the security warning (it only appears because the certificate is self-signed - the connection is still secure), and voila - you'll be presented with the phpMyAdmin login screen. You're done.

A note on the RewriteRule

Apache's VirtualHost behaviour can be a little confusing at first. It attempts to match the incoming http host header to a known VirtualHost (e.g. looking for a corresponding ServerName or ServerAlias somewhere in its loaded configuration files). If it can't find a match, it serves the default - the first loaded - VirtualHost, running on the appropriate port. This is important because we've only got one VirtualHost running on SSL - and can only have one, so by simply visiting any domain on your server or indeed its IP address over https, an attacker would be presented with your phpmyadmin site. To counter this, we use a simple RewriteRule to check if the http host header is correct - if not, we rewrite the request to serve nothing. It's not the most elegant solution, but it works well.

Wrapping Up

This post should serve as a quick guide to avoiding some of the more common pitfalls encountered when setting up phpMyAdmin, though of course it doesn't instantly solve all your security concerns at once. You've still got to make sure your database passwords are secure and you may find you out-grow the phpmyadmin-as-a-vhost model quite quickly when you need more applications running over SSL - but for a few minutes work it's well worth doing and will thwart a lot of the simpler more speculative bots looking for common server weaknesses.

Related Articles

Comments

There are currently no comments.

Comments are now closed.