Best way to store encryption key on same server in Phalcon app

Hey guys,

So I was reading over the docs on how to encrypt stuff https://docs.phalconphp.com/en/3.3/crypt and also saw an example here on how to setup an encryption service. https://docs.phalconphp.com/en/3.3/crypt#service.

I'm not in love (at all) with storing a global key in this manner. Just curious what others thing of my implementation.

So I have a key that I have stored encrypted in .phalcon/somekey.gpg. When the server starts up, (and memcached is started), I run this simple command to decrypt the key by entering a password I have in my head, set it to stdout store it in an environment variable, then store it in memcached.

$ ~ SOME_KEY=$(gpg -dq --no-symkey-cache .phalcon/somekey.gpg) php -r "\$m = new Memcached(); \$m->addServer('localhost', 11211); \$m->set('SOME_KEY', getenv('SOME_KEY'));"

Then in my config.php file, I have a value for my key.

$m = new Memcached();
$m->addServer('localhost', 11211);

return new \Phalcon\Config([
    'database' => [
       // db stuff
    ],
    'application' => [
       // app stuff
    ],
    'secret_key' => $m->get('SOME_KEY')
]);

Then in my crypt service, I reference secret_key to be used. Thoughts? I'm trying to do what I can to prevent someone from gaining access somehow and then running tar / to capture the key, and I'm not storing the key in the db, but in RAM. I'm also trying to do what I can to keep the key safe on the same server (instead of calling another server to do the encryption as that (I image) would cause a significant slowdown from making many calls within the model to decrypt all the fields individually.

Also curious on anyones thoughts on key rotation and how that would look in Phalcon? I have some models setup with beforeSave() and afterFetch() with automatically decrypting and encrypting variables so data is stored encrypted at rest, but also dynamically decrypted so I don't have to manually do it all the time. How would one go about rotating an encryption key on all the data that is at rest with the beforeSave() and afterFetch() events being set the way they are?

Hey @Austin, that's an ingenius solution, but i think it all depends!

Where is the app hosted? If it's a rented/owned physical box, you can easiliy secure the pass in plaintext using linux permissions. You should setup an encrypted LVM in this case.

If it's a shared VPS host, storing the plain pass in memcached is a good idea. Although it COULD be compromised, but that's on the provider, and not something you can do about.

Is it a multi-user server (do you provide hosting services)? If it is, storing it in memcached will not help, since anyone can access that, and if you want to harden that, you are back to step one.

Regarding key rotation: If you want to use beforeFetch/afterFetch/etc, you will need some kind of flag that synchronizes key rotation for each record. That's not really performant.

I would create a console/migration task for that, where you decrypt all data to memory, then encrypt and write it back with the new key.



1.6k
edited Jun '18

@Lajos Bencz So I suppose some context would be helpful. Currently it's running on a leased VM where I have root access. It also has some other sites that I host on the VM, but none of the other sites use Memcached. They are also running under fcgi.

I think of the scenarios you listed above, I could protect myself with linux permissions. Could you detail that out a little bit more? Lets say my user that runs fcgi is cgiuser1. My web dir and all that would be under cgiuser1. The only thing I can think of that would protect it permissions wise from other users on my system would be to create a file with 0600 permissions right? But if root was compromised and they ran a tar / then they'd get my key and all the data regardless of whatever permissions level I have. Granted sticking it in a "public" environment like memcached isn't a hot idea either, but at least it's a hope that if I were attacked, the person wouldn't think to pull out all cached data in memcached (or apcu, etc). Can't stick it in an environment variable cause that's available on the disk I think in /proc/environs or something and can't use /dev/shm as both spots are on disk and woulommandd be compromised in a tar / command.

The other solution I was thinking was just buying another tiny vm and on each web request make a request to get the encryption key and store the key in memory for the duration of the script and dump it afterwards, but that also brings with it a whole new slew of security issues that need to be tackled, but maybe something like project vault would be good in that case?

With key rotation, are you saying I would set a constant in a task and do something similar to

public function beforeSave() {
        if($this->somefield) {
            $di = $this->getDI();
            $new_key = defined('NEW_KEY_ROTATION') ? NEW_KEY_ROTATION : null;
            $this->somefield = $di->getCrypt()->encryptBase64($this->somefield, $new_key);
        }
    }

    public function afterFetch() {
        if($this->somefield) {
            $di = $this->getDI();
            $this->somefield = $di->getCrypt()->decryptBase64($this->somefield);
        }
    }

And then in some task, just pull back everything and resave it?

define('NEW_KEY_ROTATION', $new_key_from_memcached_or_wherever);
$robots = Robots::find();
foreach($robots as $robot) {
    $robot->save();
}