根据此处发布的评论,不建议使用 mcrypt-extension 并将在PHP 7.2中删除。因此,我正在寻找一种加密密码的替代方法。
现在我正在使用类似
mcrypt_encrypt(MCRYPT_RIJNDAEL_128, md5($key, true), $string, MCRYPT_MODE_CBC, $iv)
我需要您的意见以最好/最有效的方式对密码进行加密,当然,加密的密码应受PHP 7.xx支持,并且也应可解密,因为我的客户确实希望选择“恢复”密码而不生成新密码之一。
根据此处发布的评论,不建议使用 mcrypt-extension 并将在PHP 7.2中删除。因此,我正在寻找一种加密密码的替代方法。
现在我正在使用类似
mcrypt_encrypt(MCRYPT_RIJNDAEL_128, md5($key, true), $string, MCRYPT_MODE_CBC, $iv)
我需要您的意见以最好/最有效的方式对密码进行加密,当然,加密的密码应受PHP 7.xx支持,并且也应可解密,因为我的客户确实希望选择“恢复”密码而不生成新密码之一。
Answers:
最佳做法是对密码进行哈希处理,以使密码不可解密。这对于可能已经获得对数据库或文件的访问权限的攻击者来说,事情变得有些困难。
如果必须加密数据并使其可解密,请访问https://paragonie.com/white-paper/2015-secure-php-data-encryption以获得安全加密/解密指南。总结该链接:
Sodium-compat
(github.com/paragonie/sodium_compat)的库可在PHP> = 5.2.4中工作
如@rqLizard所建议,您可以改用openssl_encrypt
/ openssl_decrypt
PHP函数,它提供了一个更好的替代方案来实现AES(高级加密标准),也称为Rijndael加密。
根据以下Scott在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
vsaes-256-ctr
)。OpenSSL还使用具有CBC模式的PKCS7填充,而不是mcrypt的NULL字节填充。因此,与OpenSSL相比,mcrypt更可能使您的代码容易遭受填充oracle攻击。
最后,如果您不对密文进行身份验证(先加密然后加密MAC),那么您做错了。
进一步阅读:
适用于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";
}
?>
适用于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";
}
?>
根据以上示例,我更改了以下旨在加密用户会话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的评论):
MCRYPT_RIJNDAEL_128
)是等同于AES,但是Rijndael算法256(MCRYPT_RIJNDAEL_256
)不是 AES-256作为256个指定的256位的块尺寸,而AES仅具有一个块大小:128比特。因此,MCRYPT_RIJNDAEL_256
由于mcrypt开发人员的选择,基本上具有256位()块大小的Rijndael 被错误地命名。@zaphRijndael使用不同的块大小进行加密会生成不同的加密数据。
例如,MCRYPT_RIJNDAEL_256
(不等于AES-256
)定义Rijndael块密码的另一种变体,其大小为256位,并且基于传入的密钥来确定密钥大小,其中aes-256-cbc
Rijndael是块大小为128位且密钥大小为的Rijndael 256位。因此,他们使用不同的块大小,这会产生完全不同的加密数据,因为mcrypt使用数字来指定块大小,其中OpenSSL使用数字来指定密钥大小(AES只有一个128位的块大小)。因此,基本上AES是Rijndael,其块大小为128位,密钥大小为128、192和256位。因此,最好使用AES,它在OpenSSL中称为Rijndael 128。
$session_id = rtrim($decryptedSessionId, "\0");
?最后是否可以openssl_decrypt
返回一些不需要的字符?如果加密后的变量以0结尾(即,encrypt("abc0")
?
"\0"
不是"0"
NULL字符,其ASCII码为0x00(十六进制0)。
Rijndael的纯PHP实现与phpseclib一起存在,可以作为作曲家软件包使用,并且可以在PHP 7.3上运行(由我测试)。
在phpseclib文档上有一个页面,在您输入基本变量(密码,模式,密钥大小,位大小)之后,该页面会生成示例代码。它为Rijndael,ECB,256、256输出以下内容:
用mycrypt编写的代码
$decoded = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ENCRYPT_KEY, $term, MCRYPT_MODE_ECB);
在图书馆这样工作
$rijndael = new \phpseclib\Crypt\Rijndael(\phpseclib\Crypt\Rijndael::MODE_ECB);
$rijndael->setKey(ENCRYPT_KEY);
$rijndael->setKeyLength(256);
$rijndael->disablePadding();
$rijndael->setBlockLength(256);
$decoded = $rijndael->decrypt($term);
*原$term
为base64_decoded
如此处其他答案所详述,我发现的最佳解决方案是使用OpenSSL。它内置在PHP中,您不需要任何外部库。以下是简单的示例:
加密:
function encrypt($key, $payload) {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);
}
解密:
function decrypt($key, $garble) {
list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
}
参考链接:https : //www.shift8web.ca/2017/04/how-to-encrypt-and-execute-your-php-code-with-mcrypt/
您可以使用phpseclib pollyfill软件包。您不能使用open ssl或libsodium通过rijndael 256进行加密/解密。另一个问题是,您不需要替换任何代码。
mcrypt_compat
通过运行进行安装,composer require phpseclib/mcrypt_compat
但仍然可以PHP Fatal error: Uncaught Error: Call to undefined function mcrypt_get_key_size() in /app/kohana/classes/Kohana/Encrypt.php:124
使用php 7.2.26
和Kohanaframwork。使用composer安装后是否还有其他步骤要执行?
require APPPATH . '/vendor/autoload.php';
到的底部bootstrap.php
。
您应该使用OpenSSL,mcrypt
因为它是积极开发和维护的。它提供了更好的安全性,可维护性和可移植性。其次,它执行AES加密/解密的速度更快。默认情况下,它使用PKCS7填充,但是您可以指定OPENSSL_ZERO_PADDING
是否需要它。要与32字节的二进制密钥一起使用,您可以指定aes-256-cbc
哪个明显比MCRYPT_RIJNDAEL_128
。
这是使用Mcrypt的代码示例:
用Mcrypt编写的未经身份验证的AES-256-CBC加密库,带有PKCS7填充。
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeMcryptAES
{
const CIPHER = MCRYPT_RIJNDAEL_128;
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mcrypt_create_iv($ivsize, MCRYPT_DEV_URANDOM);
// Add PKCS7 Padding
$block = mcrypt_get_block_size(self::CIPHER);
$pad = $block - (mb_strlen($message, '8bit') % $block, '8bit');
$message .= str_repeat(chr($pad), $pad);
$ciphertext = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$message,
MCRYPT_MODE_CBC,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
$plaintext = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$ciphertext,
MCRYPT_MODE_CBC,
$iv
);
$len = mb_strlen($plaintext, '8bit');
$pad = ord($plaintext[$len - 1]);
if ($pad <= 0 || $pad > $block) {
// Padding error!
return false;
}
return mb_substr($plaintext, 0, $len - $pad, '8bit');
}
}
这是使用OpenSSL编写的版本:
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeOpensslAES
{
const METHOD = 'aes-256-cbc';
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = openssl_random_pseudo_bytes($ivsize);
$ciphertext = openssl_encrypt(
$message,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
return openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
}
}
我发布此消息的时间很晚,但我希望它能对其他在PHP 7.2x版本中面临相同问题的人有所帮助。
我在PHP 7.2x上使用它,对我来说工作正常。
public function make_hash($userStr){
try{
/**
* Used and tested on PHP 7.2x, Salt has been removed manually, it is now added by PHP
*/
return password_hash($userStr, PASSWORD_BCRYPT);
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
然后使用以下功能对哈希进行身份验证:
public function varify_user($userStr,$hash){
try{
if (password_verify($userStr, $hash)) {
return true;
}
else {
return false;
}
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
//例:
//create hash from user string
$user_password = $obj->make_hash2($user_key);
并使用以下代码验证此哈希:
if($obj->varify_user($key, $user_key)){
//this is correct, you can proceed with
}
就这样
您应该使用openssl_encrypt()
功能。
我能够翻译我的加密对象
使用mcrypt获取php副本以解密旧数据。我去了http://php.net/get/php-7.1.12.tar.gz/from/a/mirror,对其进行了编译,然后添加了ext / mcrypt扩展名(configure; make; make install)。我想我也必须在php.ini中添加extenstion = mcrypt.so行。一系列脚本可构建数据的中间版本,而所有数据均未加密。
为openssl构建公用密钥和专用密钥
openssl genrsa -des3 -out pkey.pem 2048
(set a password)
openssl rsa -in pkey.pem -out pkey-pub.pem -outform PEM -pubout
要加密(使用公共密钥),请使用openssl_seal。根据我的阅读,使用RSA密钥的openssl_encrypt限制为小于密钥长度的11个字节(请参阅Thomas Horsten的http://php.net/manual/en/function.openssl-public-encrypt.php评论)
$pubKey = openssl_get_publickey(file_get_contents('./pkey-pub.pem'));
openssl_seal($pwd, $sealed, $ekeys, [ $pubKey ]);
$encryptedPassword = base64_encode($sealed);
$key = base64_encode($ekeys[0]);
您可能可以存储原始二进制文件。
解密(使用私钥)
$passphrase="passphrase here";
$privKey = openssl_get_privatekey(file_get_contents('./pkey.pem'), $passphrase);
// I base64_decode() from my db columns
openssl_open($encryptedPassword, $plain, $key, $privKey);
echo "<h3>Password=$plain</h3>";
PS您不能加密空字符串(“”)
PPS这是用于密码数据库而不用于用户验证。
password_hash
验证password_verify
?