mcrypt è deprecato, qual è l’alternativa?

L’estensione mcrypt è deprecata e verrà rimossa in PHP 7.2 in base al commento pubblicato qui . Quindi sto cercando un modo alternativo per crittografare le password.

In questo momento sto usando qualcosa del genere

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

Ho bisogno della tua opinione per il modo migliore / più forte per crittografare le password, la password crittografata dovrebbe ovviamente essere supportata da PHP 7.xx e dovrebbe anche essere decifrabile perché i miei clienti vogliono avere un’opzione per “recuperare” le loro password senza generare una nuova uno.

È consigliabile eseguire l’hash delle password in modo che non siano decifrabili. Ciò rende le cose leggermente più difficili per gli attaccanti che potrebbero aver avuto accesso al tuo database o ai tuoi file.

Se è necessario crittografare i dati e renderli decodificati, è disponibile una guida per proteggere la crittografia / decrittografia all’indirizzo https://paragonie.com/white-paper/2015-secure-php-data-encryption . Per riassumere questo link:

  • Usa Libsodium : un’estensione PHP
  • Se non puoi usare Libsodium, usa defuse / php-encryption – Codice PHP diritto
  • Se non è ansible utilizzare Libsodium o defondere / php-encryption, utilizzare OpenSSL : molti server lo avranno già installato. In caso contrario, può essere compilato con –with-openssl [= DIR]

Come suggerito da @ rqLizard , è ansible utilizzare invece le funzioni PHP openssl_encrypt / openssl_decrypt che fornisce un’alternativa molto migliore per implementare AES (The Advanced Encryption Standard) noto anche come crittografia Rijndael.

In base al seguente commento di Scott su php.net :

Se stai scrivendo il codice per crittografare / crittografare i dati nel 2015, dovresti usare openssl_encrypt() e openssl_decrypt() . La libreria sottostante ( libmcrypt ) è stata abbandonata dal 2007 ed ha prestazioni molto peggiori di OpenSSL (che sfrutta AES-NI su processori moderni ed è sicuro per la cache temporanea).

Inoltre, MCRYPT_RIJNDAEL_256 non è AES-256 , è una variante diversa del codice a blocchi Rijndael. Se si desidera AES-256 in mcrypt , è necessario utilizzare MCRYPT_RIJNDAEL_128 con una chiave a 32 byte. OpenSSL rende più ovvio quale modalità stai usando (es. aes-128-cbc vs aes-256-ctr ).

OpenSSL utilizza anche il padding PKCS7 con modalità CBC anziché il byte padding NULL di mcrypt. Pertanto, mcrypt ha maggiori probabilità di rendere il tuo codice vulnerabile agli attacchi di Oracle oracle di OpenSSL.

Infine, se non stai autenticando i tuoi ciphertexts (Encrypt Then MAC), stai sbagliando.

Ulteriori letture:

  • Utilizzo corretto della crittografia e dell’autenticazione (per sviluppatori PHP) .
  • Se stai digitando la parola MCRYPT nel tuo codice PHP, stai sbagliando .

Esempi di codice

Esempio 1

Crittografia autenticata AES nell’esempio della modalità GCM per PHP 7.1+

  

Esempio # 2

Esempio di crittografia autenticata AES per PHP 5.6+

  

Esempio # 3

In base agli esempi precedenti, ho modificato il seguente codice che mira a crittografare l’ID di sessione dell’utente:

 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()); } } 

in:

 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(); } } 

Per chiarire, la modifica di cui sopra non è una conversione vera poiché le due crittografie utilizzano una diversa dimensione di blocco e un diverso dato crittografato. Inoltre, il padding predefinito è diverso, MCRYPT_RIJNDAEL supporta solo padding null non standard. @zaph


Note aggiuntive (dai commenti di @ zaph):

  • Rijndael 128 ( MCRYPT_RIJNDAEL_128 ) è equivalente a AES , tuttavia Rijndael 256 ( MCRYPT_RIJNDAEL_256 ) non è AES-256 poiché il 256 specifica una dimensione di blocco di 256-bit, mentre AES ha solo una dimensione di blocco: 128-bit. Quindi, in pratica Rijndael con una dimensione del blocco di 256 bit ( MCRYPT_RIJNDAEL_256 ) è stato erroneamente chiamato a causa delle scelte degli sviluppatori di mcrypt . @zaph
  • Rijndael con una dimensione di blocco di 256 può essere meno sicuro rispetto a una dimensione di blocco di 128 bit perché quest’ultimo ha avuto molte più recensioni e usi. In secondo luogo, l’interoperabilità è ostacasting dal fatto che AES è generalmente disponibile, dove Rijndael con una dimensione di blocco di 256 bit non lo è.
  • La crittografia con dimensioni di blocco diverse per Rijndael produce dati crittografati diversi.

    Ad esempio, MCRYPT_RIJNDAEL_256 (non equivalente a AES-256 ) definisce una variante diversa del cifrario a blocchi Rijndael con dimensione di 256 bit e una dimensione della chiave basata sulla chiave passata, dove aes-256-cbc è Rijndael con una dimensione di blocco di 128 bit con una dimensione della chiave di 256 bit. Pertanto utilizzano blocchi di dimensioni diverse che producono dati crittografati completamente diversi poiché mcrypt utilizza il numero per specificare la dimensione del blocco, dove OpenSSL ha utilizzato il numero per specificare la dimensione della chiave (AES ha solo una dimensione di blocco di 128 bit). Quindi fondamentalmente AES è Rijndael con una dimensione di blocco di 128 bit e dimensioni di chiave di 128, 192 e 256 bit. Quindi è meglio usare AES, che si chiama Rijndael 128 in OpenSSL.

È ansible utilizzare il pacchetto pollyfill phpseclib. Non è ansible utilizzare open ssl o libsodium per crittografare / decodificare con rijndael 256. Un altro problema, non è necessario sostituire alcun codice.

Come indicato, non è necessario memorizzare le password degli utenti in un formato decifrabile. La crittografia reversibile fornisce agli hacker un percorso semplice per scoprire le password dei tuoi utenti, che si estende a mettere a rischio gli account degli utenti su altri siti nel caso in cui utilizzino la stessa password.

PHP fornisce una coppia di potenti funzioni per la crittografia hash a salti unidirezionali, password_hash() e password_verify() . Dato che l’hash viene automaticamente salato a caso, gli hacker non possono utilizzare le tabelle precompilate degli hash delle password per decodificare la password. Imposta l’opzione PASSWORD_DEFAULT e le versioni future di PHP utilizzeranno automaticamente algoritmi più potenti per generare hash delle password senza dover aggiornare il codice.

Dovresti usare la funzione openssl_encrypt() .

Sono stato in grado di tradurre il mio object Crypto

  • Ottieni una copia di php con mcrypt per decifrare i vecchi dati. Sono andato su http://php.net/get/php-7.1.12.tar.gz/from/a/mirror , lo ho compilato, quindi ho aggiunto l’estensione ext / mcrypt (configure; make; make install). Penso di aver dovuto aggiungere anche la riga extenstion = mcrypt.so al php.ini. Una serie di script per creare versioni intermedie dei dati con tutti i dati non criptati.

  • Costruisci una chiave pubblica e privata per openssl

     openssl genrsa -des3 -out pkey.pem 2048 (set a password) openssl rsa -in pkey.pem -out pkey-pub.pem -outform PEM -pubout 
  • Per cifrare (usando la chiave pubblica) usa openssl_seal. Da quello che ho letto, openssl_encrypt utilizzando una chiave RSA è limitato a 11 byte in meno della lunghezza della chiave (vedi http://php.net/manual/en/function.openssl-public-encrypt.php commento di 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]); 

Probabilmente potresti memorizzare il file binario non elaborato.

  • Per decifrare (usando la chiave privata)

     $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 Non puoi crittografare la stringa vuota (“”)

PPS Questo è per un database di password non per la convalida dell’utente.

Dovresti usare OpenSSL su mcrypt poiché è triggersmente sviluppato e mantenuto. Fornisce maggiore sicurezza, manutenibilità e portabilità. In secondo luogo, esegue la crittografia / decrittografia AES molto più velocemente. Utilizza il riempimento PKCS7 per impostazione predefinita, ma è ansible specificare OPENSSL_ZERO_PADDING se necessario. Per utilizzare una chiave binaria a 32 byte, è ansible specificare aes-256-cbc che è molto più ovvio di MCRYPT_RIJNDAEL_128 .

Ecco l’esempio di codice usando Mcrypt:

Libreria di crittografia AES-256-CBC non autenticata scritta in Mcrypt con riempimento 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'); } } 

Ed ecco la versione scritta usando 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 ); } } 

Fonte: se stai digitando la parola MCRYPT nel tuo codice PHP, stai sbagliando .