Today security and privacy are becoming more and more important. We’re not only hearing about password leaks but leaks of sensitive information. Servers will always get hacked, this can’t be avoided. But encrypting the data stored on those servers can drastically reduce the damage.
In this article, I will discuss how to store and retrieve encrypted data in the WordPress database. Before doing so, I will discuss the difference between hashing and encryption along with a few other considerations.
Hashing vs Encryption
In WordPress, we use hashing a lot as part of our security. Nonces, which I covered in a recent article for Torque, use hashes. A nonce is an example of a cryptographic hash. It is generated from a combination of public and secret data — including salts from wp-config.php, run through the md5 hashing algorithm. It’s verified by running the same process and comparing that result to the nonce being verified.
Passwords are another example of when hashing is used in WordPress. Before being stored in the database, a password is hashed. Then when a user enters a password, the same hash is applied to it, and then the results are compared.
There are two important things to note about hashing that were mentioned in both cases.
The first is that both cases use a salt. Hashing a string alone isn’t very secure, since the result of a hashing algorithm is predictable. You need to add a secret section of the string that is known to the system but not the outside world. These come from constants in wp-config.php and make a nonce created on the site for the same action different from a nonce set on your site.
The other thing to notice is that this process goes one way. We never “unhash” a string, which we hope is effectively impossible. Instead, we compare the value of the nonce or password from the incoming HTTP request to the result of the same hashing process.
That’s all well and good, but what if we want to be able to protect some content, and still allow it to be read by an authorized user? That’s when reversible encryption comes in, which I will cover shortly.
Don’t Forget SSL
Now we will go over reversible encryption for data stored in the WordPress database. It’s worth mentioning that if you’re allowing browsers to communicate with your site via the insecure HTTP protocol, instead of HTTPS, this is kind of pointless, every WordPress site should use an SSL certificate and force the use of HTTP. This is an essential first step to securing data.
Also, this is going to require an encryption key that needs to be stored somewhere, probably wp-config.php. Ensuring no one can access wp-config is very important and requires proper security for your server.
Encrypting Options
To introduce you to how to use reversible encryption in PHP, I’m going to show you how to create a class that will store and read data from the WordPress options table using encryption. This example is going to use an established crypto library, instead of making our own.
You could definitely create a lighter-weight encryption library yourself. That said, it is really hard to consider all the different weaknesses that could happen in your design. It’s best to stick to something that has been developed for a few years, is open-source, and has a lot of contributors.
Before using any of these examples, make sure you have the defuse/php-encryption PHP package included in your project. I installed it using Composer, via packagist, but you could use Git or copypaste.
Please note this library requires PHP 5.4 or later.
The first thing we will need to do is generate and store an encryption key. You can do that using the Key class provided by the library we are using, like this:
<?php $key = \Defuse\Crypto\Key::createNewRandomKey(); $key_string = $key->saveToAsciiSafeString();
You might be tempted to just use your own “random” string. Don’t. The string generated by this class is not only actually random, but it’s also designed to be used by the rest of this library. Simply using “hiroy” or some random set of characters you hit on your keyboard will not work.
You also may be tempted to store this key in the database, but that’s not a great idea. The point of encrypting data is that if the data is ever exposed, it’s not useful. Not useful without the encryption key that is. Encryption mitigates the effect of unauthorized access to data. Storing the key with that data is like installing a lock on a door, and hanging the key on a hook next to it.
So, once you’ve generated the key, place it in a constant, defined in your wp-config.php file.
Next, let’s create a class that wraps WordPress’ get_option(), add_option() and update_option(), functions, but adds encryption. But before we do that let’s, look at how to use the encrypt and decrypt methods of the Crypto class provided by the encrypt-php library.
Both methods are fairly simple. We can encrypt a value like this:
\Defuse\Crypto\Crypto::encrypt( 'hi-roy', $key );
Then, we can decrypt it by using the same key, like this:
$decyrpted = \Defuse\Crypto\Crypto::decrypt( 'hi-roy', $key );
Pretty simple, right? So let’s wrap it up in a class, that assumes the encryption key is stored in the JOSH_ENCRYPT_KEY constant:
<?php class Josh_Encyrpted_Option{ public static function update_option( $option, $value, $autoload = null ){ return update_option( $option, Crypto::encrypt( $value, JOSH_ENCRYPT_KEY ), $autoload ); } public static function add_option( $option, $value, $autoload = 'yes' ){ add_option( $option, Crypto::encrypt( $value, JOSH_ENCRYPT_KEY ), '', $autoload ); } public static function get_option( $option, $default = false ){ $encrypted = get_option( $option, $default ); if ( $encrypted === $default ){ return $default; }else{ try { $value = Crypto::decrypt($encrypted, JOSH_ENCRYPT_KEY ); return $value; } catch ( WrongKeyOrModifiedCiphertextException $e) { return new WP_Error( $e->getCode(), $e->getMessage() ); } } } }
This provides wrappers for WordPress’ option API that encrypt and decrypt values. It’s pretty simple, but keep in mind this is going to make sanitization and validation via register_setting or pre_update_option hooks difficult. I would recommend locating that step before and after this class is used.
Going Further
This article is a basic introduction on how to do reversible encryption in PHP. A way to store options with encryption is useful, as lots of things stored in options for plugins — like API keys are sensitive. But, we’ve just scratched the surface of what you can do.
I encourage you to take this further. For example, what about user meta that requires a user to provide their own key, so the data could only be read by that user? Also, since WordPress’ database API WPDB can be replaced, you can make a compatible database class by extending WPDB that encrypted and decrypted all queries.
3 Comments