it-swarm-eu.dev

mcrypt è deprecato, qual è l'alternativa?

L'estensione mcrypt è deprecato sarà rimosso in PHP 7.2 secondo il commento pubblicato qui . Quindi sto cercando un modo alternativo per crittografare le password.

In questo momento sto usando qualcosa di simile

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 supportata da PHP 7.xx e dovrebbe anche essere decifrabile perché i miei clienti vogliono avere un'opzione per 'recuperare' le loro password senza generarne una nuova.

78
Piet

È 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 decrittografabili, è disponibile una guida per proteggere la crittografia/decrittografia all'indirizzo https://paragonie.com/white-paper/2015-secure-php-data- crittografia . Per riassumere questo link:

  • Usa Libsodium - A PHP estensione
  • Se non puoi usare Libsodium, usa defuse/php-encryption - Dritto PHP codice
  • Se non puoi usare Libsodium o defuse/php-encryption, usa OpenSSL - Molti server avranno già installato questo. In caso contrario, può essere compilato con --with-openssl [= DIR]
38
Phil

Come suggerito da @ rqLizard , puoi usare openssl_encrypt / openssl_decrypt PHP funzioni invece che fornisce un'alternativa molto migliore all'implementazione AES (The Advanced Encryption Standard) noto anche come crittografia Rijndael.

Come di seguito Il 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 è molto peggiore di OpenSSL (che sfrutta AES-NI sui processori moderni ed è sicuro per la cache-timing).

Inoltre, MCRYPT_RIJNDAEL_256 non è AES-256, è una variante diversa del codice a blocchi di 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à si sta utilizzando (ad esempio aes-128-cbc vs aes-256-ctr).

OpenSSL usa anche il padding PKCS7 con modalità CBC piuttosto che il byte padding NULL di mcrypt. Pertanto, mcrypt ha maggiori probabilità di rendere il codice vulnerabile agli attacchi Oracle di riempimento rispetto a OpenSSL.

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

Ulteriori letture:

Esempi di codice

Esempio 1

Crittografia autenticata AES nell'esempio della modalità GCM per 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";
}
?>

Esempio # 2

Esempio di crittografia autenticata AES per 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";
}
?>

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 sopra riportata non è una conversione vera in quanto le due crittografie utilizzano una diversa dimensione del 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 una sola dimensione del blocco: 128-bit. Quindi fondamentalmente Rijndael con una dimensione di blocco di 256 bit (MCRYPT_RIJNDAEL_256) è stato erroneamente chiamato a causa delle scelte degli mcrypt sviluppatori. @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à è ostacolata 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 diversi dati crittografati.

    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 un 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 in pratica 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.

21
kenorb

L'implementazione Pure-PHP di Rijndael esiste con phpseclib disponibile come pacchetto di composizione e funziona su PHP 7.3 (testato da me).

C'è una pagina sui documenti phpseclib, che genera codice di esempio dopo aver inserito le variabili di base (cifrario, modalità, dimensione della chiave, dimensione del bit). Emette quanto segue per Rijndael, ECB, 256, 256:

un codice con mycrypt

$decoded = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ENCRYPT_KEY, $term, MCRYPT_MODE_ECB);

funziona così con la libreria

$rijndael = new \phpseclib\Crypt\Rijndael(\phpseclib\Crypt\Rijndael::MODE_ECB);
$rijndael->setKey(ENCRYPT_KEY);
$rijndael->setKeyLength(256);
$rijndael->disablePadding();
$rijndael->setBlockLength(256);

$decoded = $rijndael->decrypt($term);

* $term era base64_decoded

6
Pentium10

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

4

Dovresti usare OpenSSL su mcrypt perché è attivamente sviluppato e mantenuto. Fornisce maggiore sicurezza, manutenibilità e portabilità. In secondo luogo esegue la crittografia/decrittografia AES molto più velocemente. Utilizza la spaziatura PKCS7 per impostazione predefinita, ma è possibile specificare OPENSSL_ZERO_PADDING se necessario. Per utilizzare con una chiave binaria a 32 byte, è possibile 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 PHP Codice, stai sbagliando .

3
kenorb

Come sottolineato, non è necessario memorizzare le password degli utenti in un formato decifrabile. La crittografia reversibile fornisce agli hacker un percorso semplice per trovare le password dei tuoi utenti, che si estende a mettere a rischio gli account degli utenti in altri siti se utilizzano 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 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.

1

Dovresti usare la funzione openssl_encrypt() .

1
rqLizard

Come dettagliato da altre risposte qui, la soluzione migliore che ho trovato sta usando OpenSSL. È incorporato in PHP e non è necessaria alcuna libreria esterna. Ecco alcuni semplici esempi:

Per crittografare:

function encrypt($key, $payload) {
  $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
  $encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
  return base64_encode($encrypted . '::' . $iv);
}

Per decifrare:

function decrypt($key, $garble) {
    list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
    return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
}

Link di riferimento: https://www.shift8web.ca/2017/04/how-to-encrypt-and-execute-your-php-code-with-mcrypt/

1

Sono stato in grado di tradurre il mio oggetto Crypto

  • Ottieni una copia di php con mcrypt per decifrare i vecchi dati. Sono andato a http://php.net/get/php-7.1.12.tar.gz/from/a/mirror , lo ho compilato, quindi ha 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 (utilizzando 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 "<h3>Password=$plain</h3>";
    

Post scriptum Non è possibile crittografare la stringa vuota ("")

P.P.S. Questo è per un database di password non per la convalida dell'utente.

0