Which PHP Mcrypt Cipher Is Safest

Which PHP mcrypt cipher is safest?

If unsure use AES (also known as "Rijndael") with a 128-bit key. If you have developed some kind of fetish about key size then you could fulfill your irrational qualms by selecting a larger key, e.g. 192 or 256 bits; the extra cost is not high (+40% workload for AES-256, compared to AES-128, and it takes a very very fast network to actually observe that difference).

Beware that, regardless of the key size chosen, the correct mcrypt cipher for AES is always MCRYPT_RIJNDAEL_128. This is because the AES standard refers to the flavor of the Rijndael cipher with a 128-bit block size. If you want AES-256, you need to use MCRYPT_RIJNDAEL_128 with a 256-bit (32 byte) key, not MCRYPT_RIJNDAEL_256.

AES was published in 1998 and adopted by the US government as a federal standard in 2001, and it shows no sign of weakness nowadays. Some mathematical properties were found later on, but they do not impact actual security; mostly, they highlight that we have some relatively precise knowledge on why AES is secure. No other symmetric encryption algorithm has received as much attention (by thousands of talented cryptographers) than AES.

Most security issues come from how the cryptographic algorithm is used, not the algorithm itself. Use a proper chaining mode, add a MAC, manage padding, and most of all handle the keys securely. If you got all of this right (which is much more tricky than what it seems) then it becomes time to worry about choosing Rijndael, Twofish or whatever.

PHP: Mcrypt - which mode?

mcrypt actually implements more modes than listed, you can use the string names to access them:

  • cbcCBC mode
  • cfb – 8-bit CFB mode;
  • ncfb – block-size CFB mode;
  • nofbOFB mode (not ofb);
  • ctrCTR mode.

The modes differ in implementation details, so their suitability depends on your data and environment.

Padding:

  • CBC mode only encrypts complete blocks, so mcrypt pads your plaintext with zero bytes unless you implement your own padding.

  • CFB, OFB and CTR modes encrypt messages of any length.

Initialization vector:

  • CBC and CFB modes require a random IV (don't use MCRYPT_RAND).

  • OFB mode merely requires a unique IV (e.g. a global counter, maybe the database primary key if rows are never modified or deleted).

  • CTR requires that each counter block is unique (not just the IV of the message, which is the first counter block, but the rest, formed by incrementing the counter block by 1 for each block of the message).

More information in the NIST recommendations.

There are differences in performance which should be unimportant in PHP, such as whether encryption or decryption can be parallelized and how many cipher iterations are used per block (usually one, but 16 in 8-bit CFB mode).

There are differences in malleability which should be unimportant because you will apply a MAC.

And there may be differences in their security, but for that you should consult a cryptographer.

How safe is mcrypt encoding

Encoding a string using base64 for login information is not increasing security.

To implement a secure method, I suggest to use a key binding encryption just like OpenSSL.

PHP also support it, you may define a key in your php program and encrypt your cookie with that, I also suggest to use a dynamic key(i.e 6 digit date 170417), in case you need the cookie to be completely undiscoverable!

Take a look at openssl_encrypt and openssl_get_cipher_method()

Using PHP mcrypt with Rijndael/AES

ecb is the simplest and has weaknesses so it is not recommended (http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation). cbc is considered significantly stronger than ecb. Some of the others may be even stronger than cbc but they are all stream related so cbc should suit your needs.

From... http://us.php.net/manual/en/mcrypt.constants.php...

  • MCRYPT_MODE_ECB (electronic codebook) is suitable for random data, such as encrypting other keys. Since data there is short and random, the disadvantages of ECB have a favorable negative effect.
  • MCRYPT_MODE_CBC (cipher block chaining) is especially suitable for encrypting files where the security is increased over ECB significantly.
  • MCRYPT_MODE_CFB (cipher feedback) is the best mode for encrypting byte streams where single bytes must be encrypted.
  • MCRYPT_MODE_OFB (output feedback, in 8bit) is comparable to CFB, but can be used in applications where error propagation cannot be tolerated. It's insecure (because it operates in 8bit mode) so it is not recommended to use it.
  • MCRYPT_MODE_NOFB (output feedback, in nbit) is comparable to OFB, but more secure because it operates on the block size of the algorithm.
  • MCRYPT_MODE_STREAM is an extra mode to include some stream algorithms like "WAKE" or "RC4".

I'm not sure why MCRYPT_RAND is recommended against but it may be because the system random number generator on many systems is not considered to be truely random. There are only two alternatives and they may not be available depending on your system and PHP version. From... http://php.net/manual/en/function.mcrypt-create-iv.php...

  • The IV source can be MCRYPT_RAND (system random number generator), MCRYPT_DEV_RANDOM (read data from /dev/random) and MCRYPT_DEV_URANDOM (read data from /dev/urandom). Prior to 5.3.0, MCRYPT_RAND was the only one supported on Windows.

The code below is just a quick sample. It works but I can't attest to it's strength.


<?php

// Test code

$objEncManager = new DataEncryptor();

$sensitiveData = "7890";
echo "Raw Data: _" . $sensitiveData . "_<br><br>";

$encryptedData = $objEncManager->mcryptEncryptString( $sensitiveData );
echo "Enc Data: _" . $encryptedData . "_<br><br>";
echo "Enc Data length: " . strlen( $encryptedData) . "<br><br>";

$decryptedData = $objEncManager->mcryptDecryptString( $encryptedData, $objEncManager->lastIv );
echo "D-enc Data: _" . $decryptedData . "_<br><br>";

echo "IV: _" . $objEncManager->lastIv . "_<br><br>";

/*
* Note: These functions do not accurately handle cases where the data
* being encrypted have trailing whitespace so the data
* encrypted by them must not have any. Leading whitespace is okay.
*
* Note: If your data needs to be passed through a non-binary safe medium you should
* base64_encode it but this makes the data about 33% larger.
*
* Note: The decryption IV must be the same as the encryption IV so the encryption
* IV must be stored or transmitted with the encrypted data.
* From (http://php.net/manual/en/function.mcrypt-create-iv.php)...
* "The IV is only meant to give an alternative seed to the encryption routines.
* This IV does not need to be secret at all, though it can be desirable.
* You even can send it along with your ciphertext without losing security."
*
* Note: These methods don't do any error checking on the success of the various mcrypt functions
*/
class DataEncryptor
{
const MY_MCRYPT_CIPHER = MCRYPT_RIJNDAEL_256;
const MY_MCRYPT_MODE = MCRYPT_MODE_CBC;
const MY_MCRYPT_KEY_STRING = "1234567890-abcDEFGHUzyxwvutsrqpo"; // This should be a random string, recommended 32 bytes

public $lastIv = '';

public function __construct()
{
// do nothing
}

/**
* Accepts a plaintext string and returns the encrypted version
*/
public function mcryptEncryptString( $stringToEncrypt, $base64encoded = true )
{
// Set the initialization vector
$iv_size = mcrypt_get_iv_size( self::MY_MCRYPT_CIPHER, self::MY_MCRYPT_MODE );
$iv = mcrypt_create_iv( $iv_size, MCRYPT_RAND );
$this->lastIv = $iv;

// Encrypt the data
$encryptedData = mcrypt_encrypt( self::MY_MCRYPT_CIPHER, self::MY_MCRYPT_KEY_STRING, $stringToEncrypt , self::MY_MCRYPT_MODE , $iv );

// Data may need to be passed through a non-binary safe medium so base64_encode it if necessary. (makes data about 33% larger)
if ( $base64encoded ) {
$encryptedData = base64_encode( $encryptedData );
$this->lastIv = base64_encode( $iv );
} else {
$this->lastIv = $iv;
}

// Return the encrypted data
return $encryptedData;
}

/**
* Accepts a plaintext string and returns the encrypted version
*/
public function mcryptDecryptString( $stringToDecrypt, $iv, $base64encoded = true )
{
// Note: the decryption IV must be the same as the encryption IV so the encryption IV must be stored during encryption

// The data may have been base64_encoded so decode it if necessary (must come before the decrypt)
if ( $base64encoded ) {
$stringToDecrypt = base64_decode( $stringToDecrypt );
$iv = base64_decode( $iv );
}

// Decrypt the data
$decryptedData = mcrypt_decrypt( self::MY_MCRYPT_CIPHER, self::MY_MCRYPT_KEY_STRING, $stringToDecrypt, self::MY_MCRYPT_MODE, $iv );

// Return the decrypted data
return rtrim( $decryptedData ); // the rtrim is needed to remove padding added during encryption
}

}
?>

Picking encryption cipher for mcrypt

MCRYPT_RIJNDAEL_128 is AES-128, MCRYPT_RIJNDAEL_256 is AES-256 - just another name:

[...]The standard comprises three block
ciphers, AES-128, AES-192 and AES-256,
adopted from a larger collection
originally published as Rijndael.originally published as Rijndael.[...]

[...]The Rijndael cipher was developed by
two Belgian cryptographers, Joan
Daemen and Vincent Rijmen, and
submitted by them to the AES selection
process. Rijndael (pronounced "Rhine
dall") is a wordplay with the names of
the two inventors.[...]

The \x00 characters you encounter at the end of the decrypted string are the padding required for some block ciphers (with ECB being such a block cipher). Mcyrpt uses NULL-padding internally if the input data needs to be padded to the required block length. There are other padding modes available (which have to be user-coded when using Mcyrpt), namely PKCS7, ANSI X.923 or ISO 10126. NULL-padding is problematic when encrypting binary data that may end with one or more \x00 characters because you can't detect where the data ends and the padding starts - the other padding modes mentioned solve this kind of problem. If you're encrypting character data (strings) you can easily trim off the trailing \x00 by using $data = trim($data, "\x00");.

To decrypt the data you sent to a consumer, the consumer would need to know the IV (initialization vector) ($iv), the algorithm used (MCRYPT_RIJNDAEL_256/AES-256), the encryption mode (ECB), the secret encryption key ($key) and the padding mode used (NULL-padding). The IV can be transmitted with the encrypted data as it does not need to be kept secret:

The IV must be known to the recipient
of the encrypted information to be
able to decrypt it. This can be
ensured in a number of ways: by
transmitting the IV along with the
ciphertext, by agreeing on it
beforehand during the key exchange or
the handshake, by calculating it
(usually incrementally), or by
measuring such parameters as current
time (used in hardware authentication
tokens such as RSA SecurID, VASCO
Digipass, etc.), IDs such as sender's
and/or recipient's address or ID, file
ID, the packet, sector or cluster
number, etc. A number of variables can
be combined or hashed together,
depending on the protocol.depending on the protocol.

mcrypt is deprecated, what is the alternative?

It's best practice to hash passwords so they are not decryptable. This makes things slightly more difficult for attackers that may have gained access to your database or files.

If you must encrypt your data and have it decryptable, a guide to secure encryption/decryption is available at https://paragonie.com/white-paper/2015-secure-php-data-encryption. To summarize that link:

  • Use Libsodium - A PHP extension
  • If you can't use Libsodium, use defuse/php-encryption - Straight PHP code
  • If you can't use Libsodium or defuse/php-encryption, use OpenSSL - A lot of servers will already have this installed. If not, it can be compiled with --with-openssl[=DIR]

PHP: Most secure (decryptable) encryption method?

You probably want MCRYPT_RIJNDAEL_256. Rijndael with blocksizes of 128, 192 and 256 bit is a generalization of AES which only supports a blocksize of 128 bit.

See: http://us.php.net/manual/en/mcrypt.ciphers.php and http://us.php.net/manual/en/book.mcrypt.php

Using PHP mcrypt with Rijndael/AES

ecb is the simplest and has weaknesses so it is not recommended (http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation). cbc is considered significantly stronger than ecb. Some of the others may be even stronger than cbc but they are all stream related so cbc should suit your needs.

From... http://us.php.net/manual/en/mcrypt.constants.php...

  • MCRYPT_MODE_ECB (electronic codebook) is suitable for random data, such as encrypting other keys. Since data there is short and random, the disadvantages of ECB have a favorable negative effect.
  • MCRYPT_MODE_CBC (cipher block chaining) is especially suitable for encrypting files where the security is increased over ECB significantly.
  • MCRYPT_MODE_CFB (cipher feedback) is the best mode for encrypting byte streams where single bytes must be encrypted.
  • MCRYPT_MODE_OFB (output feedback, in 8bit) is comparable to CFB, but can be used in applications where error propagation cannot be tolerated. It's insecure (because it operates in 8bit mode) so it is not recommended to use it.
  • MCRYPT_MODE_NOFB (output feedback, in nbit) is comparable to OFB, but more secure because it operates on the block size of the algorithm.
  • MCRYPT_MODE_STREAM is an extra mode to include some stream algorithms like "WAKE" or "RC4".

I'm not sure why MCRYPT_RAND is recommended against but it may be because the system random number generator on many systems is not considered to be truely random. There are only two alternatives and they may not be available depending on your system and PHP version. From... http://php.net/manual/en/function.mcrypt-create-iv.php...

  • The IV source can be MCRYPT_RAND (system random number generator), MCRYPT_DEV_RANDOM (read data from /dev/random) and MCRYPT_DEV_URANDOM (read data from /dev/urandom). Prior to 5.3.0, MCRYPT_RAND was the only one supported on Windows.

The code below is just a quick sample. It works but I can't attest to it's strength.


<?php

// Test code

$objEncManager = new DataEncryptor();

$sensitiveData = "7890";
echo "Raw Data: _" . $sensitiveData . "_<br><br>";

$encryptedData = $objEncManager->mcryptEncryptString( $sensitiveData );
echo "Enc Data: _" . $encryptedData . "_<br><br>";
echo "Enc Data length: " . strlen( $encryptedData) . "<br><br>";

$decryptedData = $objEncManager->mcryptDecryptString( $encryptedData, $objEncManager->lastIv );
echo "D-enc Data: _" . $decryptedData . "_<br><br>";

echo "IV: _" . $objEncManager->lastIv . "_<br><br>";

/*
* Note: These functions do not accurately handle cases where the data
* being encrypted have trailing whitespace so the data
* encrypted by them must not have any. Leading whitespace is okay.
*
* Note: If your data needs to be passed through a non-binary safe medium you should
* base64_encode it but this makes the data about 33% larger.
*
* Note: The decryption IV must be the same as the encryption IV so the encryption
* IV must be stored or transmitted with the encrypted data.
* From (http://php.net/manual/en/function.mcrypt-create-iv.php)...
* "The IV is only meant to give an alternative seed to the encryption routines.
* This IV does not need to be secret at all, though it can be desirable.
* You even can send it along with your ciphertext without losing security."
*
* Note: These methods don't do any error checking on the success of the various mcrypt functions
*/
class DataEncryptor
{
const MY_MCRYPT_CIPHER = MCRYPT_RIJNDAEL_256;
const MY_MCRYPT_MODE = MCRYPT_MODE_CBC;
const MY_MCRYPT_KEY_STRING = "1234567890-abcDEFGHUzyxwvutsrqpo"; // This should be a random string, recommended 32 bytes

public $lastIv = '';

public function __construct()
{
// do nothing
}

/**
* Accepts a plaintext string and returns the encrypted version
*/
public function mcryptEncryptString( $stringToEncrypt, $base64encoded = true )
{
// Set the initialization vector
$iv_size = mcrypt_get_iv_size( self::MY_MCRYPT_CIPHER, self::MY_MCRYPT_MODE );
$iv = mcrypt_create_iv( $iv_size, MCRYPT_RAND );
$this->lastIv = $iv;

// Encrypt the data
$encryptedData = mcrypt_encrypt( self::MY_MCRYPT_CIPHER, self::MY_MCRYPT_KEY_STRING, $stringToEncrypt , self::MY_MCRYPT_MODE , $iv );

// Data may need to be passed through a non-binary safe medium so base64_encode it if necessary. (makes data about 33% larger)
if ( $base64encoded ) {
$encryptedData = base64_encode( $encryptedData );
$this->lastIv = base64_encode( $iv );
} else {
$this->lastIv = $iv;
}

// Return the encrypted data
return $encryptedData;
}

/**
* Accepts a plaintext string and returns the encrypted version
*/
public function mcryptDecryptString( $stringToDecrypt, $iv, $base64encoded = true )
{
// Note: the decryption IV must be the same as the encryption IV so the encryption IV must be stored during encryption

// The data may have been base64_encoded so decode it if necessary (must come before the decrypt)
if ( $base64encoded ) {
$stringToDecrypt = base64_decode( $stringToDecrypt );
$iv = base64_decode( $iv );
}

// Decrypt the data
$decryptedData = mcrypt_decrypt( self::MY_MCRYPT_CIPHER, self::MY_MCRYPT_KEY_STRING, $stringToDecrypt, self::MY_MCRYPT_MODE, $iv );

// Return the decrypted data
return rtrim( $decryptedData ); // the rtrim is needed to remove padding added during encryption
}

}
?>


Related Topics



Leave a reply



Submit