Как предлагает @rqLizard , вместо этого вы можете использовать функции openssl_encrypt
/ openssl_decrypt
PHP, которые предоставляют гораздо лучшую альтернативу для реализации AES (расширенного стандарта шифрования), также известного как шифрование Rijndael.
Согласно следующему комментарию Скотта на php.net :
Если вы пишете код для шифрования / шифрования данных в 2015 году, вам следует использовать openssl_encrypt()
и openssl_decrypt()
. Базовая библиотека ( libmcrypt
) заброшена с 2007 года и работает намного хуже, чем OpenSSL (который использует AES-NI
современные процессоры и безопасен по времени кэширования).
Кроме того, MCRYPT_RIJNDAEL_256
нет AES-256
, это другой вариант блочного шифра Rijndael. Если вы хотите AES-256
в mcrypt
, вы должны использовать MCRYPT_RIJNDAEL_128
с ключом 32 байта. OpenSSL делает более очевидным, какой режим вы используете (например, aes-128-cbc
vs aes-256-ctr
).
OpenSSL также использует заполнение PKCS7 в режиме CBC, а не заполнение байтов NULL в mcrypt. Таким образом, mcrypt с большей вероятностью сделает ваш код уязвимым для атак оракула заполнения, чем OpenSSL.
Наконец, если вы не аутентифицируете свои зашифрованные тексты (Encrypt Then MAC), вы делаете это неправильно.
Дальнейшее чтение:
Примеры кода
Пример # 1
Пример аутентифицированного шифрования AES в режиме GCM для 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";
}
?>
Пример # 2
Пример аутентифицированного шифрования AES для 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";
}
?>
Пример # 3
На основе приведенных выше примеров я изменил следующий код, который нацелен на шифрование идентификатора сеанса пользователя:
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());
}
}
в:
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();
}
}
Чтобы уточнить, вышеупомянутое изменение не является истинным преобразованием, поскольку два шифрования используют разный размер блока и разные зашифрованные данные. Кроме того, заполнение по умолчанию отличается, MCRYPT_RIJNDAEL
поддерживается только нестандартное заполнение нулями. @zaph
Дополнительные примечания (из комментариев @ zaph):
- Rijndael 128 (
MCRYPT_RIJNDAEL_128
) это эквивалентно AES , однако Rijndael 256 ( MCRYPT_RIJNDAEL_256
) не AES-256 , как 256 определяет размер блока 256 бит, в то время как AES имеет только один размер блока: 128 бит. Итак, в основном Rijndael с размером блока 256 бит ( MCRYPT_RIJNDAEL_256
) был ошибочно назван из-за выбора разработчиков mcrypt . @zaph
- Rijndael с размером блока 256 может быть менее безопасным, чем с размером блока 128 бит, потому что у последнего было гораздо больше обзоров и применений. Во-вторых, совместимость затрудняется тем, что в то время как AES обычно доступен, а Rijndael с размером блока 256 бит - нет.
Шифрование с разными размерами блоков для Rijndael производит разные зашифрованные данные.
Например, MCRYPT_RIJNDAEL_256
(не эквивалентно AES-256
) определяет другой вариант блочного шифра Rijndael с размером 256 бит и размером ключа на основе переданного ключа, где aes-256-cbc
Rijndael с размером блока 128 бит с размером ключа 256 бит. Поэтому они используют разные размеры блоков, которые создают совершенно разные зашифрованные данные, поскольку mcrypt использует число для указания размера блока, а OpenSSL использует число для указания размера ключа (AES имеет только один размер блока из 128 бит). Итак, в основном AES - это Rijndael с размером блока 128 бит и размером ключа 128, 192 и 256 бит. Поэтому лучше использовать AES, который в OpenSSL называется Rijndael 128.
password_hash
и не проверятьpassword_verify
?