@rqLizard가 제안한 대로 Rijndael 암호화라고도 하는 AES (고급 암호화 표준) 를 구현하는 훨씬 더 나은 대안을 제공하는 openssl_encrypt
/ openssl_decrypt
PHP 함수를 대신 사용할 수 있습니다 .
php.net에서 다음 Scott의 의견에 따라 :
2015 년에 데이터를 암호화 / 암호화하는 코드를 작성하는 경우 openssl_encrypt()
및 openssl_decrypt()
. 기본 라이브러리 ( libmcrypt
)는 2007 년부터 폐기되었으며 OpenSSL ( AES-NI
최신 프로세서 를 활용 하고 캐시 타이밍에 안전함) 보다 성능이 훨씬 떨어졌습니다 .
또한 MCRYPT_RIJNDAEL_256
아니다 AES-256
그것은 Rijndael을 암호화 블록의 다른 변형 예이다. AES-256
에서 원하는 경우 32 바이트 키와 함께 mcrypt
사용해야 MCRYPT_RIJNDAEL_128
합니다. OpenSSL은 사용중인 모드 (예 : aes-128-cbc
vs aes-256-ctr
) 를보다 명확하게 보여줍니다 .
OpenSSL은 또한 mcrypt의 NULL 바이트 패딩 대신 CBC 모드로 PKCS7 패딩을 사용합니다. 따라서 mcrypt는 OpenSSL보다 패딩 오라클 공격에 코드를 취약하게 만들 가능성이 높습니다.
마지막으로 암호문을 인증하지 않으면 (Encrypt Then MAC) 잘못된 것입니다.
추가 읽기 :
코드 예
예 1
PHP 7.1+ 용 GCM 모드의 AES 인증 암호화 예제
<?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
PHP 5.6+ 용 AES 인증 암호화 예제
<?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
위의 예를 기반으로 사용자의 세션 ID를 암호화하는 것을 목표로하는 다음 코드를 변경했습니다.
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 비트의 블록 크기 만 있습니다. 따라서 기본적으로 블록 크기가 256 비트 ( MCRYPT_RIJNDAEL_256
) 인 Rijndael 은 mcrypt 개발자 의 선택으로 인해 잘못 명명되었습니다 . @zaph
- 블록 크기가 256 인 Rijndael은 128 비트의 블록 크기보다 안전하지 않을 수 있습니다. 후자는 훨씬 더 많은 리뷰와 사용을 가지고 있기 때문입니다. 둘째, AES는 일반적으로 사용 가능하지만 블록 크기가 256 비트 인 Rijndael은 사용할 수 없다는 점에서 상호 운용성이 저해됩니다.
Rijndael에 대해 서로 다른 블록 크기로 암호화하면 서로 다른 암호화 된 데이터가 생성됩니다.
예를 들어, MCRYPT_RIJNDAEL_256
(와 동일하지 않음 AES-256
)은 256 비트 크기와 전달 된 키를 기반으로하는 키 크기를 가진 Rijndael 블록 암호의 다른 변형을 정의합니다. 여기서 aes-256-cbc
Rijndael은 키 크기가 128 비트이고 블록 크기가 다음과 같습니다. 256 비트. 따라서 그들은 mcrypt가 블록 크기를 지정하기 위해 숫자를 사용하기 때문에 완전히 다른 암호화 된 데이터를 생성하는 다른 블록 크기를 사용하고 있습니다. 여기서 OpenSSL은 키 크기를 지정하기 위해 숫자를 사용했습니다 (AES는 128 비트의 한 블록 크기 만 있습니다). 따라서 기본적으로 AES는 블록 크기가 128 비트이고 키 크기가 128, 192 및 256 비트 인 Rijndael입니다. 따라서 OpenSSL에서 Rijndael 128이라고하는 AES를 사용하는 것이 좋습니다.
password_hash
하고 검증하지password_verify
않습니까?