mcrypt est obsolète, quelle est l’alternative?

L’extension mcrypt est obsolète sera supprimée dans PHP 7.2 selon le commentaire posté ici . Je cherche donc une autre façon de chiffrer les mots de passe.

En ce moment, j’utilise quelque chose comme

mcrypt_encrypt(MCRYPT_RIJNDAEL_128, md5($key, true), $ssortingng, MCRYPT_MODE_CBC, $iv) 

J’ai besoin de votre avis pour le meilleur moyen de chiffrer les mots de passe, le mot de passe chiffré devrait bien sûr être supporté par PHP 7.xx et devrait également pouvoir être déchiffré car mes clients souhaitent pouvoir récupérer leurs mots de passe sans générer de nouveaux mots de passe. un.

    Il est recommandé de hacher les mots de passe pour qu’ils ne soient pas décryptables. Cela rend les choses un peu plus difficiles pour les pirates qui peuvent avoir access à votre firebase database ou à vos fichiers.

    Si vous devez chiffrer vos données et les rendre déchiffrables, un guide de chiffrement / déchiffrement sécurisé est disponible à l’ adresse https://paragonie.com/white-paper/2015-secure-php-data-encryption . Pour résumer ce lien:

    • Utilisez Libsodium – Une extension PHP
    • Si vous ne pouvez pas utiliser Libsodium, utilisez defuse / php-encryption – Code PHP droit
    • Si vous ne pouvez pas utiliser Libsodium ou désamorcer / chiffrer php, utilisez OpenSSL : de nombreux serveurs auront déjà installé ce logiciel. Sinon, il peut être compilé avec –with-openssl [= DIR]

    Comme suggéré par @rqLizard , vous pouvez utiliser les fonctions PHP openssl_encrypt / openssl_decrypt qui offrent une alternative bien meilleure à l’implémentation d’ AES (Advanced Encryption Standard), également connue sous le nom de cryptage Rijndael.

    Selon le commentaire suivant de Scott sur php.net :

    Si vous écrivez du code pour chiffrer / chiffrer 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 que OpenSSL (qui tire parti d’ AES-NI sur les processeurs modernes et est sécurisé contre le cache).

    De plus, MCRYPT_RIJNDAEL_256 n’est pas AES-256 , c’est une variante différente du chiffrement de bloc Rijndael. Si vous voulez AES-256 dans mcrypt , vous devez utiliser MCRYPT_RIJNDAEL_128 avec une clé de 32 octets. OpenSSL rend plus évident le mode que vous utilisez (c. aes-128-cbc 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 par bourrage que sous OpenSSL.

    Enfin, si vous n’authentifiez pas vos ciphertexts (Encrypt Then MAC), vous le faites mal.

    Lectures complémentaires:

    • Utilisation correcte du cryptage et de l’authentification (pour les développeurs PHP) .
    • Si vous tapez le mot MCRYPT dans votre code PHP, vous le faites mal .

    Exemples de code

    Exemple 1

    AES Authenticated Encryption dans l’exemple de mode GCM pour PHP 7.1+

      

    Exemple # 2

    Exemple de chiffrement AES authentifié pour PHP 5.6+

      

    Exemple # 3

    Basé sur les 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 ssortingng. * * @param $session_id * @return ssortingng */ 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 ssortingng */ 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 ssortingng. $decryptedSessionId = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decoded, MCRYPT_MODE_CBC, $iv); // Trim the whitespace from the end. $session_id = rsortingm($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 ssortingng. * * @param $session_id * @return ssortingng */ 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 ssortingng */ 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 ssortingng. $decryptedSessionId = openssl_decrypt($decoded, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv); // Trim the whitespace from the end. $session_id = rsortingm($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 puisque les deux cryptages utilisent une taille de bloc différente et une donnée cryptée différente. En outre, le remplissage par défaut est différent, MCRYPT_RIJNDAEL ne prend en charge que le remplissage null non standard. @zaph


    Notes complémentaires (issues 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 que AES ne possède qu’une seule taille de bloc: 128 bits. Donc, Rijndael avec une taille de bloc de 256 bits ( MCRYPT_RIJNDAEL_256 ) a été nommé par erreur en raison des choix des développeurs de mcrypt . @zaph
    • Rijndael avec une taille de bloc de 256 peut être moins sécurisé 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 gênée par le fait que, bien que l’AES soit généralement disponible, où 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 (non équivalent à AES-256 ) définit une variante différente du chiffrement de bloc Rijndael avec une taille de 256 bits et une taille de clé basée sur la clé passée, où aes-256-cbc correspond à Rijndael avec une taille de bloc. de 128 bits avec une taille de clé de 256 bits. Par conséquent, ils utilisent différentes tailles de bloc qui produisent des données cryptées entièrement différentes car mcrypt utilise le numéro pour spécifier la taille du bloc, où OpenSSL utilisait le nombre pour spécifier la taille de la clé (AES ne possède qu’une taille 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. Il est donc préférable d’utiliser AES, appelé Rijndael 128 dans OpenSSL.

    Vous pouvez utiliser le package phpseclib pollyfill. Vous ne pouvez pas utiliser open ssl ou libsodium pour chiffrer / déchiffrer avec rijndael 256. Un autre problème, vous n’avez pas besoin de remplacer de code.

    Comme indiqué précédemment, vous ne devriez pas stocker les mots de passe de vos utilisateurs dans un format décryptable. Le chiffrement réversible permet aux pirates informatiques de trouver facilement les mots de passe de vos utilisateurs, ce qui permet d’exposer les comptes de vos utilisateurs à d’autres sites s’ils utilisent le même mot de passe.

    PHP fournit une paire de fonctions puissantes pour le chiffrement de hachage unidirectionnel, à salage aléatoire, password_hash() et password_verify() . Étant donné que le hachage est automatiquement aléatoire, il est impossible pour les pirates d’utiliser des tables de hachage de mot de passe précompilées pour procéder au reverse engineering du mot de passe. Définissez l’option PASSWORD_DEFAULT et les futures versions de PHP utiliseront automatiquement des algorithmes plus puissants pour générer des hachages de mots de passe sans que vous ayez à mettre à jour votre code.

    Vous devez utiliser la fonction openssl_encrypt() .

    J’ai pu traduire mon object Crypto

    • Obtenez une copie de php avec mcrypt pour décrypter les anciennes données. Je suis allé sur http://php.net/get/php-7.1.12.tar.gz/from/a/mirror , je l’ai compilé, puis j’ai ajouté l’extension ext / mcrypt (configure, make, make install). Je pense que je devais également append la ligne extenstion = mcrypt.so au fichier php.ini. Une série de scripts pour construire des versions intermédiaires des données avec toutes les données non chiffrées.

    • Construire une clé publique et privée pour openssl

       openssl genrsa -des3 -out pkey.pem 2048 (set a password) openssl rsa -in pkey.pem -out pkey-pub.pem -outform PEM -pubout 
    • Pour chiffrer (en utilisant une clé publique), utilisez openssl_seal. D’après ce que j’ai lu, openssl_encrypt utilisant une clé RSA est limité à 11 octets de moins que la longueur de la clé (voir http://php.net/manual/en/function.openssl-public-encrypt.php commentaire de Thomas Horsten)

       $pubKey = openssl_get_publickey(file_get_contents('./pkey-pub.pem')); openssl_seal($pwd, $sealed, $ekeys, [ $pubKey ]); $encryptedPassword = base64_encode($sealed); $key = base64_encode($ekeys[0]); 

    Vous pourriez probablement stocker le binary brut.

    • Pour déchiffrer (en utilisant la clé privée)

       $passphrase="passphrase here"; $privKey = openssl_get_privatekey(file_get_contents('./pkey.pem'), $passphrase); // I base64_decode() from my db columns openssl_open($encryptedPassword, $plain, $key, $privKey); echo "

      Password=$plain

      ";

    PS Vous ne pouvez pas chiffrer la chaîne vide (“”)

    PPS Ceci est pour une firebase database de mots de passe pas pour la validation des utilisateurs.

    Vous devez utiliser OpenSSL sur mcrypt car il est activement développé et maintenu. Il offre une sécurité, une maintenabilité et une portabilité améliorées. Deuxièmement, il effectue le cryptage / décryptage AES beaucoup plus rapidement. Il utilise le remplissage PKCS7 par défaut, mais vous pouvez spécifier OPENSSL_ZERO_PADDING si vous en avez besoin. Pour utiliser une clé binary de 32 octets, vous pouvez spécifier aes-256-cbc ce qui est beaucoup plus évident que MCRYPT_RIJNDAEL_128 .

    Voici l’exemple de code utilisant Mcrypt:

    Bibliothèque de cryptage AES-256-CBC non authentifiée écrite en Mcrypt avec remplissage PKCS7.

     /** * This library is unsafe because it does not MAC after encrypting */ class UnsafeMcryptAES { const CIPHER = MCRYPT_RIJNDAEL_128; public static function encrypt($message, $key) { if (mb_strlen($key, '8bit') !== 32) { throw new Exception("Needs a 256-bit key!"); } $ivsize = mcrypt_get_iv_size(self::CIPHER); $iv = mcrypt_create_iv($ivsize, MCRYPT_DEV_URANDOM); // Add PKCS7 Padding $block = mcrypt_get_block_size(self::CIPHER); $pad = $block - (mb_strlen($message, '8bit') % $block, '8bit'); $message .= str_repeat(chr($pad), $pad); $ciphertext = mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $message, MCRYPT_MODE_CBC, $iv ); return $iv . $ciphertext; } public static function decrypt($message, $key) { if (mb_strlen($key, '8bit') !== 32) { throw new Exception("Needs a 256-bit key!"); } $ivsize = mcrypt_get_iv_size(self::CIPHER); $iv = mb_substr($message, 0, $ivsize, '8bit'); $ciphertext = mb_substr($message, $ivsize, null, '8bit'); $plaintext = mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $key, $ciphertext, MCRYPT_MODE_CBC, $iv ); $len = mb_strlen($plaintext, '8bit'); $pad = ord($plaintext[$len - 1]); if ($pad <= 0 || $pad > $block) { // Padding error! return false; } return mb_substr($plaintext, 0, $len - $pad, '8bit'); } } 

    Et voici la version écrite en utilisant OpenSSL:

     /** * This library is unsafe because it does not MAC after encrypting */ class UnsafeOpensslAES { const METHOD = 'aes-256-cbc'; public static function encrypt($message, $key) { if (mb_strlen($key, '8bit') !== 32) { throw new Exception("Needs a 256-bit key!"); } $ivsize = openssl_cipher_iv_length(self::METHOD); $iv = openssl_random_pseudo_bytes($ivsize); $ciphertext = openssl_encrypt( $message, self::METHOD, $key, OPENSSL_RAW_DATA, $iv ); return $iv . $ciphertext; } public static function decrypt($message, $key) { if (mb_strlen($key, '8bit') !== 32) { throw new Exception("Needs a 256-bit key!"); } $ivsize = openssl_cipher_iv_length(self::METHOD); $iv = mb_substr($message, 0, $ivsize, '8bit'); $ciphertext = mb_substr($message, $ivsize, null, '8bit'); return openssl_decrypt( $ciphertext, self::METHOD, $key, OPENSSL_RAW_DATA, $iv ); } } 

    Source: Si vous tapez le mot MCRYPT dans votre code PHP, vous le faites mal .