Como lo sugiere @rqLizard , puede usar funciones openssl_encrypt
/ openssl_decrypt
PHP en su lugar, lo que proporciona una alternativa mucho mejor para implementar AES (The Advanced Encryption Standard), también conocido como cifrado Rijndael.
Según el siguiente comentario de Scott en php.net :
Si está escribiendo código para cifrar / cifrar datos en 2015, debe usar openssl_encrypt()
y openssl_decrypt()
. La biblioteca subyacente ( libmcrypt
) ha sido abandonada desde 2007 y funciona mucho peor que OpenSSL (que aprovechaAES-NI
los procesadores modernos y es seguro para el tiempo de caché).
Además, MCRYPT_RIJNDAEL_256
no lo es AES-256
, es una variante diferente del cifrado de bloques de Rijndael. Si desea que AES-256
en mcrypt
, usted tiene que utilizar MCRYPT_RIJNDAEL_128
con una clave de 32 bytes. OpenSSL hace que sea más obvio qué modo está utilizando (es decir, aes-128-cbc
vs aes-256-ctr
).
OpenSSL también usa el relleno PKCS7 con el modo CBC en lugar del relleno de bytes NULL de mcrypt. Por lo tanto, es más probable que mcrypt haga que su código sea más vulnerable a los ataques de relleno de Oracle que OpenSSL.
Finalmente, si no está autenticando sus textos cifrados (Encriptar luego MAC), lo está haciendo mal.
Otras lecturas:
Ejemplos de código
Ejemplo 1
Ejemplo de cifrado autenticado AES en modo GCM para 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";
}
?>
Ejemplo # 2
Ejemplo de cifrado autenticado AES para 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";
}
?>
Ejemplo # 3
Según los ejemplos anteriores, he cambiado el siguiente código que tiene como objetivo encriptar la identificación de sesión del usuario:
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());
}
}
dentro:
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();
}
}
Para aclarar, el cambio anterior no es una verdadera conversión, ya que los dos cifrados usan un tamaño de bloque diferente y datos cifrados diferentes. Además, el relleno predeterminado es diferente, MCRYPT_RIJNDAEL
solo admite relleno nulo no estándar. @zaph
Notas adicionales (de los comentarios de @ zaph):
- Rijndael 128 (
MCRYPT_RIJNDAEL_128
) es equivalente a AES , sin embargo, Rijndael 256 ( MCRYPT_RIJNDAEL_256
) no es AES-256 ya que 256 especifica un tamaño de bloque de 256 bits, mientras que AES tiene solo un tamaño de bloque: 128 bits. Entonces, básicamente, Rijndael con un tamaño de bloque de 256 bits ( MCRYPT_RIJNDAEL_256
) ha sido nombrado erróneamente debido a las elecciones de los desarrolladores de mcrypt . @zaph
- Rijndael con un tamaño de bloque de 256 puede ser menos seguro que con un tamaño de bloque de 128 bits porque este último ha tenido muchas más revisiones y usos. En segundo lugar, la interoperabilidad se ve obstaculizada porque, si bien AES está generalmente disponible, Rijndael con un tamaño de bloque de 256 bits no lo está.
El cifrado con diferentes tamaños de bloque para Rijndael produce diferentes datos cifrados.
Por ejemplo, MCRYPT_RIJNDAEL_256
(no equivalente a AES-256
) define una variante diferente del cifrado de bloque de Rijndael con un tamaño de 256 bits y un tamaño de clave basado en la clave pasada, donde aes-256-cbc
es Rijndael con un tamaño de bloque de 128 bits con un tamaño de clave de 256 bits. Por lo tanto, están usando diferentes tamaños de bloque que producen datos encriptados completamente diferentes ya que mcrypt usa el número para especificar el tamaño del bloque, donde OpenSSL usó el número para especificar el tamaño de la clave (AES solo tiene un tamaño de bloque de 128 bits). Básicamente, AES es Rijndael con un tamaño de bloque de 128 bits y tamaños de clave de 128, 192 y 256 bits. Por lo tanto, es mejor usar AES, que se llama Rijndael 128 en OpenSSL.
password_hash
y verificarlospassword_verify
?