2

I have created the following encryption process which I will be using to store CD keys inside my database. Before I use it, however, I need to make sure that this is as secure as possible.

From what I see, there are two primary concerns:

  • The storage of the private key must be extremely secure. If someone gets a hold of the primary key, they can decrypt all the data in my database

  • The encryption method must be as secure as possible

Is there a better way to securely store data in this scenario? If not, how can I ensure that this process is as secure as possible?

Here's my code:

public static function encrypt_key($key)
{
        $iv_size = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CBC);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
        return $iv . openssl_encrypt ($key, 'AES-256-CBC', '32 bit private key', 0, $iv);
}

public static function decrypt_key($key)
{
        $iv_size = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CBC);
        $iv = substr($key, 0, $iv_size);
        return openssl_decrypt(substr($key, $iv_size), 'AES-256-CBC', '32 bit private key', 0, $iv);
}
3

There are several improvements that can be made to the functions you've given, based on my knowledge.

Obvious threats

First, and most important, keep your private key secure, and make it external to the function definition (e.g., a second argument). I would recommend spending significant effort solving this problem. Is this per-user encryption, or application-wide encryption? If it's per-user, then why not use a key derivation algorithm, such as PBKDF2? If it's application-wide, then good luck with secure storage - I've thought about it before, and it seems non-trivial for a typical web app. Just make sure that you don't store the private key along with the source, nor (obviously) in the same database.

Also, as an aside: never use symmetric encryption for anything that's password-like. Use a one-way hash instead. I'm assuming, for this answer, that you need to actually recover the original CD key, rather than just verify a match.

Second, I'm dubious of the use of MCRYPT_RAND. It sounds like it might be the same PRNG as rand(), which is not ideal. Generating the IV with MCRYPT_DEV_URANDOM is what I'd recommend - /dev/random and /dev/urandom are both considered top-notch sources of cryptographic random numbers on Unix-based systems. Note that you don't generally want to use /dev/random for a web application, because it would open up an easy DoS vector - and /dev/urandom should suffice outside of embedded environments, anyway.

Other implementation issues

Now, with the major vulnerabilities that I can spot taken care of, a few more fine-grained details:

You're mixing OpenSSL and mcrypt functions. Also, openssl_encrypt is not documented in the official PHP docs (I suspect it's documented for C somewhere, though...). It might be better to use mcrypt_encrypt instead, since it's explicitly documented for PHP.

You're mixing CAST-256 and AES-256, as well. You should probably stick with AES-256 throughout, since that's the generally-advised standard algorithm for strong symmetric encryption. Note that AES-256 is MCRYPT_RIJNDAEL_128 used with a 256-bit key, and is NOT the same as Rijndael-256, which is something that I hadn't previously realized (the more you know!).

I'm not sure about the preferred cipher mode, but CBC is one that I have seen recommended for AES, at least. Hopefully someone else will have some input.

  • By per-user, do you mean as in using the user's password to encrypt their keys? – dspacejs Jul 20 '15 at 12:45
  • Using the user's password to derive a per-user private key, yes. Hence the suggestion to use Password-Based Key Derivation Function 2 (PBKDF2) :). It's a technique used by many sites that need to encrypt user data in a way that only that one user can decrypt, such as LastPass. – Ethan Kaminski Jul 20 '15 at 12:51
  • Okay, but what if the user's password is cracked? Is there an alternative solution? – dspacejs Jul 20 '15 at 13:02
  • Hmm... you're right, passwords in normal usage are going to be a weak link. Then again, in the event of a security breach, the contents of the webserver might be leaked, as well as the database itself. An approach that might work is to combine the user's password with some server-provided data, and then use that combination in the KDF. That way, if a database-only breach occurs, users with a password of "password" would still be protected (since the server's secret is still secure). However, this is getting into territory that's beyond my current expertise, so I might have missed something. – Ethan Kaminski Jul 20 '15 at 13:09
  • I was thinking something similar. I'm also going to PM you with a seperate question. – dspacejs Jul 20 '15 at 13:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.