I'm not sure if you're aware of the keychain mechanism in MacOS X... what I'm proposing is something along those lines. It would require a process running as a different user than the rest of PHP.
A suggested implementation in Apache would be to create a socketpair(), fork before Apache calls setuid()/setgid(), then make a setuid()/setgid() call to a different user. This process would manipulate a flat (XML?) mode 0600 file which contains a keychain or set of keychains (essentially hash tables).
Most likely this would also require a program (either setuid with locking or communicating over a Unix domain socket) to manipulate keychains.
So, given that brief sketch of a possible manipulation, here's how it would actually work. The syntax is all for example, and designed for a maximum degree of readability. Obviously the real syntax could be different, but this is all to serve as an example.
The user would have a utility to manipulate keychains. For the time being let's call it 'keychainadm'. They would start by creating a keychain:
$ keychainadm
create keychain 'foo'
Keychain 'foo' created
Now, obviously there would need to be mechanisms to ensure that the user who creates a given keychain is the only one who can access it. The easiest way would be to implement 'keychainadm' as a setuid process which can manipulate the keychain file (with some sort of sanitized locking), make a getuid() call, which will return the real, not effective uid of the caller, and have all keychains owned by that UID. These are all very specific implementation details, but rest assured that there would be some degree of user level authentication. If a Unix domain socket implementation were used, for example, then every keychain would need a password. So, for the sake of argument, here's an alternative syntax for the create statement, one for a system based around passwords instead of UIDs:
create keychain 'foo' with password 'bar';
Keychain 'foo' created
In which case any subsequent calls to keychainadm would ask for a password:
$ keychainadm -c foo
Enter password for keychain 'foo': ******
At any rate, I digress. Back to the system's use.
Now, we add a key/value pair to the keychain:
add key 'password' with value 'abc123' to keychain 'foo'
Value added to keychain 'foo'
Next, we grant access for a specific script to access the keychain:
grant read on keychain 'foo' to script '/home/jimbob/public_html/index.php';
Access granted for script '/home/jimbob/public_html/index.php' to keychain 'foo'
Now, in PHP we would need at least a function to read keychains. For now let's call it keychain_get(), and it would be used something like this
$value = keychain_get($keychain_name, $key_name);
So let's say jimbob stored his database password 'abc123' in a key named 'password' on a keychain named 'foo'. Here's how he'd use that to connect to the database:
$dbpass = keychain_get("foo", "password") or die("Couldn't read key from keychain!");
mysql_connect($dbhost, $dbuser, $dbpass);
keychain_get() will happily return jimbob's database password, provided it's called from /home/jimbob/public_html/index.php, or any subsequent scripts that jimbob decides to grant access to. But any calls to keychain_get() from scripts that haven't been granted access will fail.
Security is provided through the filesystem. The only UID able to manipulate keychains is the one the keychain manager process runs as.
This would be my proposition if there is no other means of protecting passwords on systems where the administrators have not provided for security in other ways.
I'd be happy to get a Sourceforge project going to implement something like this as well, but if its Sourceforge parge were database-driven I'd be worried about someone stealing the passwords out of my PHP scripts and messing around with the database 🙂