ตามที่แนะนำโดย@rqLizardคุณสามารถใช้ฟังก์ชันopenssl_encrypt
/ openssl_decrypt
PHP แทนซึ่งเป็นทางเลือกที่ดีกว่ามากในการใช้AES (The Advanced Encryption Standard) หรือที่เรียกว่าการเข้ารหัส Rijndael
ตามความคิดเห็นของ Scottต่อไปนี้ที่ php.net :
หากคุณกำลังเขียนโค้ดในการเข้ารหัสข้อมูล / การเข้ารหัสในปี 2015 ที่คุณควรใช้และopenssl_encrypt()
openssl_decrypt()
ไลบรารีพื้นฐาน ( libmcrypt
) ถูกละทิ้งไปตั้งแต่ปี 2550 และทำงานได้แย่กว่า 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 byte ของ mcrypt ดังนั้น mcrypt จึงมีแนวโน้มที่จะทำให้โค้ดของคุณเสี่ยงต่อการโจมตีจาก padding oracle มากกว่า OpenSSL
สุดท้ายหากคุณไม่ได้รับรองความถูกต้องของการเข้ารหัส (เข้ารหัสจากนั้น MAC) แสดงว่าคุณทำผิด
อ่านเพิ่มเติม:
ตัวอย่างโค้ด
ตัวอย่าง # 1
AES Authenticated Encryption ในโหมด 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 Authenticated สำหรับ 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 ซึ่งเรียกว่า Rijndael 128 ใน OpenSSL
password_hash
และยืนยันด้วยpassword_verify
?