Як запропонував @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
проти aes-256-ctr
).
OpenSSL також використовує підкладку PKCS7 в режимі CBC, а не байтаний байт NULL mcrypt. Таким чином, mcrypt з більшою ймовірністю зробить ваш код уразливішим для атаки Oracle, ніж OpenSSL.
Нарешті, якщо ви не автентифікуєте свої шифротексти (шифрувати потім 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
) визначає інший варіант блокового шифру Ріндейла з розміром 256 біт і розміром ключа на основі переданого ключа, де aes-256-cbc
знаходиться Rijndael з розміром блоку 128 біт з розміром ключа 256-біт. Тому вони використовують різні розміри блоків, які створюють абсолютно різні зашифровані дані, оскільки mcrypt використовує число для визначення розміру блоку, де OpenSSL використовував число для визначення розміру ключа (AES має лише один розмір блоку - 128 біт). Тому в основному AES - це Rijndael, розмір блоку - 128 біт і розміри ключів 128, 192 та 256 біт. Тому краще використовувати AES, який називається Rijndael 128 у OpenSSL.
password_hash
і перевірити їхpassword_verify
?