With the release date for PHP 7 drawing near, let's take a look at everything good and bad about developing cryptography features in PHP, what got fixed in PHP 7, and what remains to be fixed in a future version of PHP.
A Brief History of Cryptography Improvements in PHP 5
- PHP 5.2 and before
- No cross-platform CSPRNG without the mcrypt extension
- No symmetric-key encryption without mcrypt either
- PHP 5.3
- PHP gets
openssl_encrypt()
andopenssl_decrypt()
for symmetric-key encryption - Finally a cross-platform CSPRNG (
openssl_random_pseudo_bytes()
) albeit not the best one
- PHP gets
- PHP 5.4
- Improved
openssl_encrypt()
andopenssl_decrypt()
API
- Improved
- PHP 5.5
- For user account management,
password_hash()
andpassword_verify()
saves the day -
hash_pbkdf2()
for deriving encryption keys from passwords
- For user account management,
- PHP 5.6
-
hash_equals()
allows developers to prevent timing attacks
-
The PHP programming language has come a long way throughout the lifetime of PHP 5. In the beginning, you had to implement your own ciphers and CSPRNG interfaces or install a PHP extension to offer basic security. As the language matured, it has become easier to implement secure cryptographic protocols without writing your own low-level features.
Cryptography is Better in PHP 7
PHP 5 has several CSPRNG interfaces, but it's not immediately clear which one should be used (or even can):
-
mcrypt_create_iv()
is great (provided you specifyMCRYPT_DEV_URANDOM
and notMCRYPT_RAND
, but you can't access it without enabling the mcrypt extension -
openssl_random_pseudo_bytes()
taps into a userspace CSPRNG provided by the OpenSSL library rather than the operating sytem's CSPRNG (e.g./dev/urandom
) - Reading from
/dev/urandom
or usingCOM('CAPICOM.Utilities.1')
leaves a lot of room for implementation error.
To make things clear, simple, and unambiguous, PHP 7 ships with two simple functions powered by your operating system's CSRPNG.
-
string random_bytes(int)
- Generate a string of random bytes -
int random_int(int, int)
- Generate a random integer between two integers (inclusive)
If you'd like to give these new functions a whirl and don't have PHP 7 installed yet, Paragon Initiative Enterprises maintains a polyfill for PHP 5 projects called random_compat.
composer require paragonie/random_compat
# Enjoy random_bytes() and random_int() in PHP 5!
We've previously covered common uses for CSPRNGs if you need a starting point.
PECL Libsodium is Stable
The PHP bindings for the Sodium cryptography library has finally reached the stable channel in PECL. To help developers get acclimated with this library, we wrote an online book about libsodium development in PHP. You can read it for free online; downloadable copies are coming soon.
To PHP 7.1 and Beyond
There have been great leaps and bounds in the maturity of the PHP programming language, especially when it comes to cryptography features. With PHP 7.0 arriving in the coming months, we've been thinking about what we could build to make PHP 7.1 or 8.0 continue this momentum.
Make Libsodium a Core Library in 7.1 or 8.0
It's great that the PHP bindings for libsodium can be installed via pecl install libsodium
(assuming you already installed the underlying library), but in future versions of PHP it would be even better if it were bundled with the rest of the language.
Three reasons:
- Libsodium offers high-speed constant-time elliptic curve cryptography. The closest we can get to what libsodium offers with OpenSSL is slow RSA with sub-optimal padding schemes and an RC4 seal interface.
- Libsodium is currently the only way to perform AEAD in PHP. Even though OpenSSL claims to support
aes-256-gcm
, it doesn't actually work. - Libsodium, like NaCl before it, was developed to be simple, secure and hard to misuse. OpenSSL and Mcrypt are (clearly) not.
Our Chief Development Officer has opened an RFC to add libsodium to PHP.
Let's Offer a Pluggable Cryptography Interface in 7.1
One of the initiatives our team has been leading is the development of a simple cryptography frontend for 7.1. Our idea is to make the interface simple and backend-agnostic (like PDO rather than MySQLi). The current draft will support OpenSSL and Libsodium and only allow authenticated encryption (Encrypt then MAC) or AEAD constructions.
$default_crypter = new \Php\Crypto\Asymmetric;
$error_msg = $default_crypter->seal(
$message,
$recipient_public_key
);
$fips = new \Php\Crypto\Symmetric([
'driver' => 'openssl',
'cipher' => 'aes'
]);
$fips->encrypt($some_message, $some_key);
The development of a prototype is currently being discussed and conducted at paragonie/pco_prototype. Everyone is welcome to join this discussion.
Add More Hash Functions to the Existing Hash API
Although our Chief Development Officer previously opened a feature request on the PHP bug tracker for new hash functions in PHP 7, this did not get discussed in time for inclusion in 7.0. We would instead like to make it happen in PHP 7.1.
Add Argon2 to PHP's Password API
Later versions of PHP (7.1, maybe 7.2) should be updated so that password_hash()
and password_verify()
supports Argon2, the winner of the Password Hashing Competition. Whether or not this will be the new PASSWORD_DEFAULT
algorithm remains to be decided.
Note that Argon2 is already being added to libsodium.
PHP has come a long way, and it has a long road ahead of it. Whatever challenges or opportunities await us, the development team at Paragon Initiative Enterprises will continue to do everything we can to make security as easy as PIE for software developers the world over.