Comme suggéré par @rqLizard , vous pouvez utiliser les fonctions openssl_encrypt
/ openssl_decrypt
PHP à la place, ce qui offre une bien meilleure alternative pour implémenter AES (The Advanced Encryption Standard) également connu sous le nom de cryptage Rijndael.
Selon le commentaire suivant de Scott sur php.net :
Si vous écrivez du code pour crypter / crypter des données en 2015, vous devez utiliser openssl_encrypt()
et openssl_decrypt()
. La bibliothèque sous-jacente ( libmcrypt
) a été abandonnée depuis 2007, et fonctionne bien moins bien qu'OpenSSL (qui s'appuie AES-NI
sur les processeurs modernes et est sûr pour la synchronisation du cache).
Ce MCRYPT_RIJNDAEL_256
n'est pas non AES-256
plus une variante différente du chiffrement par blocs de Rijndael. Si vous voulez AES-256
en mcrypt
, vous devez utiliser MCRYPT_RIJNDAEL_128
avec une clé de 32 octets. OpenSSL rend plus évident le mode que vous utilisez (c.-à-d. aes-128-cbc
Vs aes-256-ctr
).
OpenSSL utilise également le remplissage PKCS7 avec le mode CBC plutôt que le remplissage d'octets NULL de mcrypt. Ainsi, mcrypt est plus susceptible de rendre votre code vulnérable aux attaques d'oracle de remplissage que OpenSSL.
Enfin, si vous n'authentifiez pas vos textes chiffrés (Encrypt Then MAC), vous vous trompez.
Lectures complémentaires:
Exemples de code
Exemple 1
Exemple de chiffrement authentifié AES en mode GCM pour PHP 7.1+
<?php
//$key should have been previously generated in a cryptographically safe way, like openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$cipher = "aes-128-gcm";
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
//store $cipher, $iv, and $tag for decryption later
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv, $tag);
echo $original_plaintext."\n";
}
?>
Exemple # 2
Exemple de chiffrement authentifié AES pour PHP 5.6+
<?php
//$key previously generated safely, ie: openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
//decrypt later....
$c = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
if (hash_equals($hmac, $calcmac))//PHP 5.6+ timing attack safe comparison
{
echo $original_plaintext."\n";
}
?>
Exemple # 3
Sur la base des exemples ci-dessus, j'ai changé le code suivant qui vise à chiffrer l'ID de session de l'utilisateur:
class Session {
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* @param $session_id
* @return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $session_id, MCRYPT_MODE_CBC, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($encrypt);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* @param $encryptedSessionId
* @return string
*/
public function decrypt($encryptedSessionId) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId);
// Decrypt the string.
$decryptedSessionId = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decoded, MCRYPT_MODE_CBC, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, "\0");
// Return it.
return $session_id;
}
public function _getIv() {
return md5($this->_getSalt());
}
public function _getSalt() {
return md5($this->drupal->drupalGetHashSalt());
}
}
dans:
class Session {
const SESS_CIPHER = 'aes-128-cbc';
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* @param $session_id
* @return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$ciphertext = openssl_encrypt($session_id, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($ciphertext);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* @param $encryptedSessionId
* @return string
*/
public function decrypt($encryptedSessionId) {
// Get the Drupal hash salt as a key.
$key = $this->_getSalt();
// Get the iv.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId, TRUE);
// Decrypt the string.
$decryptedSessionId = openssl_decrypt($decoded, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, '\0');
// Return it.
return $session_id;
}
public function _getIv() {
$ivlen = openssl_cipher_iv_length(self::SESS_CIPHER);
return substr(md5($this->_getSalt()), 0, $ivlen);
}
public function _getSalt() {
return $this->drupal->drupalGetHashSalt();
}
}
Pour clarifier, le changement ci-dessus n'est pas une vraie conversion car les deux cryptages utilisent une taille de bloc différente et des données cryptées différentes. En outre, le remplissage par défaut est différent, MCRYPT_RIJNDAEL
prend uniquement en charge le remplissage nul non standard. @zaph
Notes supplémentaires (à partir des commentaires de @ zaph):
- Rijndael 128 (
MCRYPT_RIJNDAEL_128
) est équivalent à AES , cependant Rijndael 256 ( MCRYPT_RIJNDAEL_256
) n'est pas AES-256 car le 256 spécifie une taille de bloc de 256 bits, alors qu'AES n'a qu'une taille de bloc: 128 bits. Donc, fondamentalement, Rijndael avec une taille de bloc de 256 bits ( MCRYPT_RIJNDAEL_256
) a été nommé à tort en raison des choix des développeurs mcrypt . @zaph
- Rijndael avec une taille de bloc de 256 peut être moins sûr qu'avec une taille de bloc de 128 bits car ce dernier a eu beaucoup plus de critiques et d'utilisations. Deuxièmement, l'interopérabilité est entravée dans la mesure où AES est généralement disponible, alors que Rijndael avec une taille de bloc de 256 bits ne l'est pas.
Le chiffrement avec différentes tailles de bloc pour Rijndael produit différentes données chiffrées.
Par exemple, MCRYPT_RIJNDAEL_256
(pas équivalent à AES-256
) définit une variante différente du chiffrement par blocs Rijndael avec une taille de 256 bits et une taille de clé basée sur la clé transmise, où aes-256-cbc
est Rijndael avec une taille de bloc de 128 bits avec une taille de clé de 256 bits. Par conséquent, ils utilisent des tailles de bloc différentes qui produisent des données chiffrées entièrement différentes car mcrypt utilise le nombre pour spécifier la taille de bloc, où OpenSSL a utilisé le nombre pour spécifier la taille de la clé (AES n'a qu'une taille de bloc de 128 bits). Donc, fondamentalement, AES est Rijndael avec une taille de bloc de 128 bits et des tailles de clé de 128, 192 et 256 bits. Par conséquent, il est préférable d'utiliser AES, qui s'appelle Rijndael 128 dans OpenSSL.
password_hash
et les vérifier avecpassword_verify
?