最佳做法是散列密码,使其不可解密。这使得对于可能已经访问您的数据库或文件的攻击者来说,事情变得更加困难。
如果您必须加密数据并使其可解密,https://paragonie.com/white-paper/2015-secure-php-data-encryption 提供安全加密/解密指南。总结一下该链接:
- 使用 Libsodium - PHP 扩展
- 如果你不能使用Libsodium,请使用defortuse/php-encryption - Straight PHP代码
- 如果你不能使用Libsodium或defiuse/php加密,请使用OpenSSL - 很多服务器已经安装了这个。如果没有,可以用 --with-openssl[=DIR] 编译
正如@rqLizard所建议的那样,您可以使用openssl_encrypt
/openssl_decrypt
PHP 函数来代替,这为实现 AES(高级加密标准)(也称为 Rijndael 加密)提供了更好的替代方案。
根据斯科特在 php.net 上的评论:
如果要在 2015 年编写代码来加密/加密数据,则应使用 和 。底层库()自2007年以来已被废弃,其性能远不如OpenSSL(它利用现代处理器并且是缓存计时安全的)。
openssl_encrypt()
openssl_decrypt()
libmcrypt
AES-NI
另外,不是 ,它是Rijndael分组密码的不同变体。如果要 in,则必须使用 32 字节的密钥。OpenSSL使您正在使用的模式更加明显(即 与 )。
MCRYPT_RIJNDAEL_256
AES-256
AES-256
mcrypt
MCRYPT_RIJNDAEL_128
aes-128-cbc
aes-256-ctr
OpenSSL还使用具有CBC模式的PKCS7填充,而不是mcrypt的NULL字节填充。因此,mcrypt比OpenSSL更有可能使您的代码容易受到填充oracle攻击。
最后,如果您没有对密文进行身份验证(然后加密MAC),那么您做错了。
延伸阅读:
代码示例
示例 #1
GCM 模式下的 AES 身份验证加密示例,适用于 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
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();
}
}
为了澄清,上述更改不是真正的转换,因为两种加密使用不同的块大小和不同的加密数据。此外,默认填充不同,仅支持非标准空填充。@zaphMCRYPT_RIJNDAEL
补充说明(来自@zaph的评论):
-
Rijndael 128 () 等同于 AES,但是 Rijndael 256 () 不是 AES-256,因为 256 指定的块大小为 256 位,而 AES 只有一个块大小:128 位。因此,基本上,由于mcrypt开发人员的选择,块大小为256位()的Rijndael被错误地命名。@zaph
MCRYPT_RIJNDAEL_128
MCRYPT_RIJNDAEL_256
MCRYPT_RIJNDAEL_256
- 块大小为256的Rijndael可能比块大小为128位的安全性低,因为后者有更多的评论和用途。其次,互操作性受到阻碍,因为虽然AES通常可用,但块大小为256位的Rijndael则不然。
-
Rijndael具有不同块大小的加密会产生不同的加密数据。
例如,(不等效于 )定义了 Rijndael 块密码的不同变体,大小为 256 位,密钥大小基于传入密钥,其中 Rijndael 的块大小为 128 位,密钥大小为 256 位。因此,他们使用不同的块大小,生成完全不同的加密数据,因为mcrypt使用数字来指定块大小,而OpenSSL使用数字来指定密钥大小(AES只有一个128位的块大小)。因此,基本上AES是Rijndael,块大小为128位,密钥大小为128,192和256位。因此,最好使用AES,它在OpenSSL中被称为Rijndael 128。
MCRYPT_RIJNDAEL_256
AES-256
aes-256-cbc
-
相当于Java中PHP的crypt函数 我正在将我的PHP代码迁移到Google App Engine - Java。因此,我需要一个相当于Java中PHP的crypt函数,因为我已将使用crypt的注册用户的所有密码存储在我的数据库中。 编辑1:这是我用于加密密码的php
-
需要有关如何从接受语言请求标头获取首选语言的示例 我需要一个代码示例或库来解析标头并返回我的首选语言。 指出: “接受语言请求标头”字段类似于“接受”,但限制首选作为请求响应的自然语言集。语言标记在第 3.10 节中定义。
-
无法在 Java 和 PHP 之间交换使用 AES-256 加密的数据 我的问题是:我在Java中加密的东西,我可以在Java中完全解密,但PHP不能解密。我用加密的内容可以使用 解密,但不能在 Java 中解密。 我想从Java应用程序发送和接收加密数据到PHP页面,所以我
-
-
Quercus是Java环境中PHP的可行替代品吗? 对于任何偶然发现这个问题的人,他们不知道是什么 - 它是用Java完成的PHP的实现。 对于我目前正在从事的项目,我们通过cgi在servlet上提供php页面(我知道它很笨拙,但这是支持遗留代码的要求