The problem with DEFINE (in PHP) - The WWW

Users browsing this thread: 1 Guest(s)
eksith
Long time nixers
Anyone who's played for even a little bit in PHP has come across this problem one time or another: Where to store the keys to your precious castle. Whether that's your database connection settings, some passwords or other secret salts, it's still a bit of a concern since it only takes one misconfiguration to allow your server to send PHP as plaintext to the visitor and (*gasp*) there go your credentials with it. This happens quite a lot, unfortunately.

The solution to this is fairly simple, if you have access to php.ini. If you run your own box (highly recommended) you already have access to this, but most new web hosts do allow it as well. If not, it may be time to switch. The tricky bit still is grabbing all those settings.

Somewhere toward the bottom of your php.ini, you can add something similar to:
Code:
[MyCustomApp]
myapp.cfg.DB_HOST = 'mysql:host=127.0.0.1;dbname=mydatabase'
myapp.cfg.DB_USER = 'dbusername'
myapp.cfg.DB_PASS = 'dbpassword'

MyCustomApp is just a label for that group of settings (this is generally good practice). And myapp.cfg. is just the prefix to your collection of custom settings.

And now in the rest of your code, you can use a simple function and an array with the list of variables you want defined.
Code:
function loadConfig( $vars = array() ) {
    foreach( $vars as $v ) {
        define( $v, get_cfg_var( "myapp.cfg.$v" ) );
    }
}

// Then call :
$cfg = array( 'DB_HOST', 'DB_USER', 'DB_PASS' );
loadConfig( $cfg );

And viola! You don't have to store the crown jewels in the most easily accessible portion of your script. The above code is the safer equivalent of :
Code:
define( 'DB_HOST', 'mysql:host=127.0.0.1;dbname=mydatabase' );
define( 'DB_USER', 'dbusername' );
define( 'DB_PASS', 'dbpassword' );
... which is the old and more vulnerable way.

There's an even more paranoid version of this possible if you host more than one site: Put one secret key in your php.ini and put another in your script. Hash the two together and you get a combined key that can be used to decrypt a separate settings file courtesy of our friend, parse_ini_string. It basically grabs all the settings in ini format (this is why it's good practice to give your settings lables) and puts it into an easily digestable array.

That's a bit more complicated, but doable. In php.ini :
Code:
sites.cfg.commonkey = 'your super secret key'

And in your script :
Code:
define( 'SCRIPT_KEY',    'Another secret key' );
define( 'INI_PATH',    '/path/to/encrypted-settings.txt' );

function loadCfg() {
    $sharedKey    = get_cfg_var( 'sites.cfg.commonkey' );
    
    $hash        = hash( 'tiger160,4', $sharedKey . SCRIPT_KEY );
    
    $file        = fopen( INI_PATH, 'r' );
    $ini        = fread( $file, filesize( INI_PATH );
    fclose( $file );
    
    $ini        = decrypt( $ini, $hash );
    
    // And get all your settings into a nicely bundled array
    $settings    = parse_ini_string( $ini );
}

Naturally, you'll need to encrypt the file first so instead of 'decrypt', you'll just use an 'encrypt' function (probably during the initial setup of your site) and paste the garbled stuff in the text file.

There are plenty of examples out there on PHP encryption, but if you feel like it, you can use a library I wrote that has additional features like password generation/hashing, IVs etc...

Any bugs, questions, better ways? Please reply.

Enjoy! :)
eksith
Long time nixers
Thanks! I aim to please ;)
venam
Administrators
That's smart. So instead of putting the credentials inside the php script you read/decrypt them from a file.
I'll remember that.
eksith
Long time nixers
Thank you! It honestly comes down to one simple fact: You can't betray what you don't know.

So the alternative becomes, where do I store something that's visible to the scripting engine, but not the web server? PHP is in a good position in this regard since, if we use something like FastCGI with Nginx, for example, that completely decouples the web server from the scripting engine. The server literally cannot serve the file, since it's not in scope.

This came about a little while back when a good friend of mine did have a misconfigured server (because his host was incompetent) and it really came down to "how much can I withstand revealing"? Let's face it, any file -- really anything in the webroot of the site -- is readable by the web server. If it's readable, it can potentially be served. No matter how unlikely, we have to account for the possibility.
eksith
Long time nixers
Well, I hope we can change that. Glad to be of service :)

I've been meaning to ask, how do I format this properly to PHP syntax? I just put the code in [ code ] blocks, but that doesn't seem to be enough. I checked the guidelines, but didn't find a hint.
desyncr
Members
What about ZendGuard (or other similar solutions)? You can safely store your credentials in PHP files and also it prevents reverse engeneering. Granted most of these solutions are propietary & paid.