Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do symmetric cryptography without hardcoded key in PHP

I'm using the PHP mcrypt library to cryptograph and store (MySQL) data using AES.

I was wondering if there is a good way to do this without having a hardcoded encryption/decryption key in my code.

If a hacker gets access to my server he will be able to see the files and my key on the code, therefore accessing all the data on the database.

Thanks.

like image 395
Alexandre Justino Avatar asked Jul 12 '16 03:07

Alexandre Justino


2 Answers

I'm using the PHP mcrypt library to cryptograph and store (MySQL) data using AES.

You may wish to reconsider your choice in cryptography library.

I was wondering if there is a good way to do this without having a hardcoded encryption/decryption key in my code.

Store it in a configuration file outside your document root? For example, defuse/php-encryption.

If a hacker gets access to my server he will be able to see the files and my key on the code, therefore accessing all the data on the database.

If a hacker gets access to your server, symmetric-key encryption cannot save you. Public-key encryption, however, can preserve confidentiality.

Using Halite, this is easy to solve:

  1. You can only encrypt on the server; never decrypt.
  2. Your secret key must be kept offline and used by a human.

Online Code (Assumes PHP 7.0 and Halite 2.1)

<?php
declare(strict_types=1);
use ParagonIE\Halite\{
    Asymmetric\Crypto as Asymmetric,
    KeyFactory
};

$publicKey = KeyFactory::loadEncryptionPublicKey("/path/to/public/key");
$encrypted = Asymmetric::seal("Whatever secret data we want", $publicKey);
// Now do whatever you need with $encrypted

Offline Code (Assumes PHP 7.0 and Halite 2.1)

<?php
declare(strict_types=1);
use ParagonIE\Halite\{
    Asymmetric\Crypto as Asymmetric,
    KeyFactory
};

$salt = ""; // Generate from random_bytes(16) once, then persist.
$password = ""; // Create a strong password

$keyPair = KeyFactory::deriveEncryptionKeyPair($password, $salt);
$secretKey = $keyPair->getSecretKey();
$publicKey = $keyPair->getPublicKey();

// To have the public key to a file to upload to the server:
   KeyFactory::save($publicKey, '/path/to/public/key');

$decrypted = Asymmetric::unseal($encrypted, $secretKey);
like image 175
Scott Arciszewski Avatar answered Nov 14 '22 22:11

Scott Arciszewski


It depends to what lengths you're willing to go, and your environment.

It's definitely a bad idea to keep the decryption key in the database - if anyone gets a hold of the database, they'll have both the decryption key and the data. By storing it on the application server, you can be certain that the above won't happen. But what if someone gets access to the application server, and then to the database through the application server? Now they have both the key and the data again. But this much you've said already.

Since you didn't mention your environment, let's assume:

  • standard LAMP stack
  • PHP runs as Apache module
  • you have a deployment tool/script to deploy your application

You could have a simple Apache configuration file that:

  • sets an environment variable to the value of your encryption key
  • gets included from the main apache configuration
  • is stored encrypted on disk/repo/wherever your deployment tool has access to

Then during deployment:

  • your deployment tool, as a part of its deployment steps, attempts to decrypt the encrypted config file and asks the deployment user for the key
  • deployment user provides the key from a system/method entirely unconnected to the running production application (e.g. offline secure password storage, etc.)
  • deployment tool copies the file to the production system and decrypts it
  • deployment tool (re)starts Apache on production systems
  • Apache loads the decrypted configuration file, setting the environment variable
  • once Apache is running, deployment tool deletes/overwrites/shreds/etc. the decrypted configuration file containing the secure encryption key

After this, the current state of things will be:

  • Apache has loaded the configuration in memory and the decrypted key is available to PHP via the environment variable
  • the secure key is not stored anywhere on the production system, it's only available in memory
  • the only way you can reload/restart Apache is via your deployment tool (which is probably what you want anyway)

How you could still be vulnerable:

  • shredding the file is secure when you're writing directly to the magnetic hard drive on a supported filesystem; it might not be as secure in a VM or SSD environment
  • an attacker with the access to the application server could dump the memory used by Apache and try to figure out how to get at the decryption key that's somewhere there
  • few seconds during deployment, while Apache loads, the file is unencrypted on the server; if an attacker has an uninterrupted access and knows what to look for, they might get lucky and find that file

Still, it's a lot safer than storing the unencrypted key on the application server, and it requires a very involved and highly sophisticated attacker to exploit. So, as I said at the beginning, it depends what lengths you want to go to.

like image 43
Unix One Avatar answered Nov 14 '22 22:11

Unix One