Theo đề xuất của @rqLizard , bạn có thể sử dụng các hàm openssl_encrypt
/ openssl_decrypt
PHP để thay thế, cung cấp giải pháp thay thế tốt hơn nhiều để triển khai AES (Tiêu chuẩn mã hóa nâng cao) còn được gọi là mã hóa Rijndael.
Theo nhận xét sau của Scott tại php.net :
Nếu bạn đang viết mã để mã hóa / mã hóa dữ liệu vào năm 2015, bạn nên sử dụng openssl_encrypt()
và openssl_decrypt()
. Thư viện cơ bản ( libmcrypt
) đã bị bỏ rơi từ năm 2007 và hoạt động kém hơn nhiều so với OpenSSL (thúc đẩy AES-NI
bộ xử lý hiện đại và an toàn về thời gian lưu vào bộ nhớ cache).
Ngoài ra, MCRYPT_RIJNDAEL_256
không phải AES-256
, đó là một biến thể khác của mật mã khối Rijndael. Nếu bạn muốn AES-256
vào mcrypt
, bạn phải sử dụng MCRYPT_RIJNDAEL_128
với khóa 32 byte. OpenSSL làm cho nó rõ ràng hơn bạn đang sử dụng chế độ nào (tức là aes-128-cbc
vs aes-256-ctr
).
OpenSSL cũng sử dụng đệm PKCS7 với chế độ CBC thay vì đệm byte NULL của mcrypt. Do đó, mcrypt có nhiều khả năng làm cho mã của bạn dễ bị tấn công bởi các cuộc tấn công thần thánh hơn OpenSSL.
Cuối cùng, nếu bạn không xác thực mật mã của mình (Mã hóa rồi MAC), bạn đang làm sai.
Đọc thêm:
Ví dụ mã
Ví dụ 1
Ví dụ về mã hóa được xác thực AES ở chế độ GCM cho 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";
}
?>
Ví dụ số 2
Ví dụ về mã hóa xác thực AES cho 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";
}
?>
Ví dụ # 3
Dựa trên các ví dụ trên, tôi đã thay đổi mã sau nhằm mục đích mã hóa id phiên của người dùng:
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());
}
}
thành:
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();
}
}
Để làm rõ, thay đổi trên không phải là một chuyển đổi thực sự vì hai mã hóa sử dụng kích thước khối khác nhau và dữ liệu được mã hóa khác nhau. Ngoài ra, phần đệm mặc định khác, MCRYPT_RIJNDAEL
chỉ hỗ trợ phần đệm rỗng không theo tiêu chuẩn. @zaph
Ghi chú bổ sung (từ nhận xét của @ zaph):
- Rijndael 128 (
MCRYPT_RIJNDAEL_128
) là tương đương với AES , tuy nhiên Rijndael 256 ( MCRYPT_RIJNDAEL_256
) không AES-256 là 256 quy định cụ thể một kích thước khối 256-bit, trong khi AES chỉ có một kích thước khối: 128-bit. Vì vậy, về cơ bản Rijndael với kích thước khối 256-bit ( MCRYPT_RIJNDAEL_256
) đã bị đặt tên nhầm do sự lựa chọn của các nhà phát triển mcrypt . @zaph
- Rijndael với kích thước khối 256 có thể kém an toàn hơn với kích thước khối 128-bit vì sau này có nhiều đánh giá và sử dụng hơn. Thứ hai, khả năng tương tác bị cản trở trong khi AES nói chung có sẵn, trong khi Rijndael với kích thước khối 256-bit thì không.
Mã hóa với các kích thước khối khác nhau cho Rijndael tạo ra các dữ liệu được mã hóa khác nhau.
Ví dụ: MCRYPT_RIJNDAEL_256
(không tương đương với AES-256
) xác định một biến thể khác của mật mã khối Rijndael với kích thước 256 bit và kích thước khóa dựa trên khóa được truyền vào, trong đó aes-256-cbc
Rijndael có kích thước khối 128 bit với kích thước khóa là 256-bit. Do đó, chúng đang sử dụng các kích thước khối khác nhau tạo ra dữ liệu được mã hóa hoàn toàn khác nhau vì mcrypt sử dụng số để chỉ định kích thước khối, trong đó OpenSSL đã sử dụng số để chỉ định kích thước khóa (AES chỉ có một kích thước khối là 128 bit). Vì vậy, về cơ bản AES là Rijndael với kích thước khối 128 bit và kích thước khóa 128, 192 và 256 bit. Do đó, tốt hơn là sử dụng AES, được gọi là Rijndael 128 trong OpenSSL.
password_hash
và xác minh chúng vớipassword_verify
?