Keeping PHP Database Passwords SecureLinux LOGO

Introduction

A common problem facing PHP developers is keeping their database password secure. If there are no interactive accounts on your web server then you can prevent the password from being discovered by an end user. If however there are other users who have shell access or filesystem access to the web server then they can discover your password with very little effort.

The Problem

To connect to a database and other resources, a password is required. The password is an arbitrary string of data which should be kept hidden from unauthorised users.

Solutions

Some solutions to this problem are presented with a short discussions about their usefulness.

Filesystem Permissions Solution

Because the web server normally runs as user nobody (or apache, httpd, whatever), the file containing the password needs to be readable by user nobody (or whatever your web server is running as) or world readable. This means any local user or user with access to the web server can read the password. The file is easily read by unauthorised users either locally or remotely. Unfortunatly, this is the solution most people currently use.

SUID Wrapper Solution

It might be possible to retrieve a password with a suid program which can sanity check the environment before returning a password, but any user with cgi access to the web server could create a false environment and trick the suid program into divulging the password, therefore this solution on it's own is not secure. Although it is not secure, it might be worth implementing this solution to aid with the following.

Cryptographic SUID Solution

A cryptographic hash on the environment might allow a check to see if the environment has been tampered with. This could make the suid solution workable. This however is a large computational requirement and requires large changes to the web server to implement. As yet, there is no prototype implementation for this solution.

Internal Storage Solution

The solution needs to build on the following principles:

The solution might also need to address the following:

Implementation of Internal Storage Solution

An apache module loads the password config file on start up. This is done as root so the config file can (and should) have restrictive permissions

When a php script wants to retrieve a password, it calls virtual() on a specific URI which is mapped to a special handler.

That handler checks to see if the request URI should be allowed access, and if so sets a note containing the password.

Issues with Internal Storage Solution

Is there a way a php script or other non-root controlled method can fool the environment of the subrequest such that the main URI can be forged/spoofed or whatever, tricking the handler into giving up the password?

Can php or another non-root controlled method read arbitrary memory from the apache process, obtaining a list of passwords from memory? This might well impact on other authentication technologies. Can a php script obtain the password a user has supplied to another request/session (ie, hijack a password being used somewhere else in the apache server).

Example Configuration

conf.d/passwordstore.conf
LoadModule passwordstore_module modules/mod_passwordstore.so

<IfModule mod_passwordstore.c>
    <Location /passwords>
        PasswordStoreDebug On
        PasswordStoreFile conf/passwords.txt
        SetHandler passwords
        Order deny,allow
        Deny from all
        Allow from 127.0.0.1
        Allow from env=get_password
    </Location>
</IfModule>
conf/passwords.txt
/sample:mysql:qwerty
sample/test.php
<?
header('Content-type: text/plain');

// Simple php example script for mod_passwordstore
apache_setenv("get_password", "true");
virtual("/passwords");
$what = "mysql";
$password = apache_note($what);
echo "The password for '$what' is '$password'\n\n";
// in real life you would connect to the database using this password rather than print it out.

echo "Sample php code:\n\n";

$code = file('index.php');
foreach($code as $line)
{
    echo $line;
}
?>

Downloads

Version 0.2

Changes

PackagePlatformDownload
source tarball any mod_passwordstore-0.2.tgz
binary rpm Enterprise Linux 2 i386 N/A. Use version 0.1.
binary rpm Enterprise Linux 3 i386 passwordstore-0.2-1.el3.i386.rpm
binary rpm Enterprise Linux 4 i386 passwordstore-0.2-1.el4.i386.rpm

Version 0.1

Version 0.1 was written for Apache 1.3. It has mostly the same functionality as version 0.2 and is only here for historical interest.

PackagePlatformDownload
source file Apache 1.3 mod_passwordstore.c
binary rpm Enterprise Linux 2 i386 mod_passwordstore-0.1-1.i386.rpm

Links