5

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.

Alexandre Justino
  • 1,716
  • 1
  • 19
  • 28
  • You would need a key somewhere. Better to store it in your code than in the database where the encrypted data lies. – Devon Bessemer Jul 12 '16 at 03:56
  • Yeah, but I was wondering if there was a more secure way. Storing the password in the code kind of breaks the whole point. The only scenario where this would be effective is if the hacker only had access to the database, not the whole server. – Alexandre Justino Jul 12 '16 at 04:00
  • 1
    Exactly. That's normally the point. If they get their hands on a database dump or access to the database server, they can't use that information. If they get access to the web server, it doesn't really matter because they'll be able to do anything including intercepting calls/data and reverse engineering – Devon Bessemer Jul 12 '16 at 04:01
  • Generally, if the server is compromised, everything is compromised so symmetrically encrypting your db data at the server doesn't really help so don't do it. – pvg Jul 12 '16 at 04:02
  • Hmm, I understand @Devon thanks! I'm working on a system that needs to save credit card information into the DB. Would you guys have any useful advices? – Alexandre Justino Jul 12 '16 at 04:04
  • 1
    Encrypting the data with AES is a normal practice for storing credit card info. You can't get around storing the key on the web server. – Devon Bessemer Jul 12 '16 at 04:05
  • Okay, I'll continue doing that. Thanks buddy! – Alexandre Justino Jul 12 '16 at 04:07
  • this might help you http://stackoverflow.com/q/3002189/1592398 – code-jaff Jul 12 '16 at 05:21
  • If you are storing credit card information then you MUST comply with the PCI-DSS requirements. Speaking personally, there are *very* few online businesses I would trust with my card details - and much prefer using a third party service like Worldpay or Paypal - which will also save you a lot of pain in development. – symcbean Jul 12 '16 at 20:15

2 Answers2

5

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);
Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
  • Thanks, that was a great answer! I've decided to use a public key to encrypt the data. This way if I get hacked the hacker won't have access to the private key. Awesome! – Alexandre Justino Jul 14 '16 at 15:53
  • I'm using PHP's `openssl_public_encrypt`. Is that a good idea? – Alexandre Justino Jul 14 '16 at 15:54
  • 1
    [Only if you use it very carefully](https://framework.zend.com/security/advisory/ZF2015-10). – Scott Arciszewski Jul 14 '16 at 17:51
  • Perhaps this is a separate question but is there any way to change the password once it's been set? Otherwise the only way I can see is decrypting everything then re-encrypting it again. – Element Zero Aug 07 '18 at 20:02
  • 1
    The example code derives the key from a password. [Protecting a key with a password without deriving the key from the password](https://github.com/defuse/php-encryption/blob/master/docs/classes/KeyProtectedByPassword.md) is possible with Defuse's PHP Encryption library. – Scott Arciszewski Aug 08 '18 at 19:52
  • Thank you. It seems Halite offers many more options than Defuse though so it seems like a tough choice. If I use Halite and I want to change the password my only choice is to decrypt, change the key and re-encrypt everything using the new key? That could be an issue if I'm storing information in a DB so just making sure there is not other options. – Element Zero Aug 09 '18 at 18:59
2

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.

Unix One
  • 1,151
  • 7
  • 14