Wie von @rqLizard vorgeschlagen , können Sie stattdessen openssl_encrypt
/ openssl_decrypt
PHP-Funktionen verwenden, die eine viel bessere Alternative zur Implementierung von AES (The Advanced Encryption Standard) darstellen, auch als Rijndael-Verschlüsselung bekannt.
Gemäß dem folgenden Kommentar von Scott auf php.net :
Wenn Sie 2015 Code zum Verschlüsseln / Verschlüsseln von Daten schreiben, sollten Sie openssl_encrypt()
und verwenden openssl_decrypt()
. Die zugrunde liegende Bibliothek ( libmcrypt
) wurde seit 2007 aufgegeben und arbeitet weitaus schlechter als OpenSSL (das AES-NI
auf modernen Prozessoren basiert und Cache-Timing-sicher ist).
Auch MCRYPT_RIJNDAEL_256
ist es nicht AES-256
, es ist eine andere Variante der Rijndael-Blockchiffre. Wenn Sie möchten , AES-256
in mcrypt
, müssen Sie verwenden MCRYPT_RIJNDAEL_128
mit einem 32-Byte - Schlüssel. OpenSSL macht deutlicher, welchen Modus Sie verwenden (dh aes-128-cbc
vs aes-256-ctr
).
OpenSSL verwendet auch PKCS7-Padding im CBC-Modus anstelle des NULL-Byte-Padding von mcrypt. Daher ist es wahrscheinlicher, dass mcrypt Ihren Code für Padding-Orakelangriffe anfällig macht als OpenSSL.
Wenn Sie Ihre Chiffretexte nicht authentifizieren (Encrypt Then MAC), machen Sie es schließlich falsch.
Weiterführende Literatur:
Codebeispiele
Beispiel 1
Beispiel für eine authentifizierte AES-Verschlüsselung im GCM-Modus für 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";
}
?>
Beispiel 2
Beispiel für eine authentifizierte AES-Verschlüsselung für 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";
}
?>
Beispiel 3
Anhand der obigen Beispiele habe ich den folgenden Code geändert, mit dem die Sitzungs-ID des Benutzers verschlüsselt werden soll:
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();
}
}
Zur Verdeutlichung ist die obige Änderung keine echte Konvertierung, da die beiden Verschlüsselungen eine unterschiedliche Blockgröße und unterschiedliche verschlüsselte Daten verwenden. Darüber hinaus ist die Standardauffüllung unterschiedlich und MCRYPT_RIJNDAEL
unterstützt nur nicht standardmäßige Nullauffüllungen. @zaph
Zusätzliche Hinweise (aus den Kommentaren von @ zaph):
- Rijndael 128 (
MCRYPT_RIJNDAEL_128
) ist äquivalent zu AES , jedoch Rijndael 256 ( MCRYPT_RIJNDAEL_256
) wird nicht AES-256 als 256 eine Blockgröße von 256-Bit gibt an , während AES nur eine Blockgröße hat: 128-Bit. Im Grunde genommen wurde Rijndael mit einer Blockgröße von 256 Bit ( MCRYPT_RIJNDAEL_256
) aufgrund der Auswahl durch die mcrypt- Entwickler fälschlicherweise benannt . @zaph
- Rijndael mit einer Blockgröße von 256 ist möglicherweise weniger sicher als mit einer Blockgröße von 128 Bit, da letztere viel mehr Überprüfungen und Verwendungen hatte. Zweitens wird die Interoperabilität dadurch behindert, dass AES allgemein verfügbar ist, Rijndael mit einer Blockgröße von 256 Bit jedoch nicht.
Die Verschlüsselung mit unterschiedlichen Blockgrößen für Rijndael erzeugt unterschiedliche verschlüsselte Daten.
Beispielsweise definiert MCRYPT_RIJNDAEL_256
(nicht äquivalent zu AES-256
) eine andere Variante der Rijndael-Blockverschlüsselung mit einer Größe von 256 Bit und eine Schlüsselgröße basierend auf dem übergebenen Schlüssel, wobei aes-256-cbc
Rijndael eine Blockgröße von 128 Bit mit einer Schlüsselgröße von ist 256 Bit. Daher verwenden sie unterschiedliche Blockgrößen, wodurch völlig unterschiedliche verschlüsselte Daten erzeugt werden, da mcrypt die Nummer zur Angabe der Blockgröße verwendet, wobei OpenSSL die Nummer zur Angabe der Schlüsselgröße verwendet (AES hat nur eine Blockgröße von 128 Bit). AES ist also im Grunde Rijndael mit einer Blockgröße von 128 Bit und Schlüsselgrößen von 128, 192 und 256 Bit. Daher ist es besser, AES zu verwenden, das in OpenSSL Rijndael 128 heißt.
password_hash
hashen und verifizierenpassword_verify
?