在你做任何进一步的事情之前,请先了解.之间的区别加密和认证,为什么你想认证加密而不仅仅是加密.
要实现经过身份验证的加密,您需要对MAC进行加密。加密和认证的顺序是非常重要的!这个问题的现有答案之一犯了这个错误;许多用PHP编写的密码库也是如此。
你应该避免实现您自己的加密技术,而是使用由密码专家编写和评审的安全库。更新:PHP7.2现在提供LibNa!为了获得最好的安全性,更新您的系统以使用PHP7.2或更高版本,并且只遵循这个答案中的libNa建议。
上面所链接的这两个库使得在您自己的库中实现经过身份验证的加密变得简单而无痛。
如果您仍然想编写和部署您自己的密码库,与互联网上的每一个密码专家的传统智慧相比,这些都是您必须采取的步骤。
加密:在CTR模式下使用AES加密。您也可以使用GCM(这消除了对单独MAC的需求)。此外,ChaCha20和Salsa 20(由)是流密码,不需要特殊的模式。
除非您选择了上面的GCM,否则您应该使用HMAC-SHA-256(或者,对于流密码器,Poly1305-大多数libnalAPI为您进行验证)来验证密文。
MAC应该包括IV和密文!
解密:除非使用Poly1305或gcm,否则重新计算密文的mac并将其与使用..如果失败,中止。
解密消息。
其他设计考虑事项:永远不要压缩任何东西。密文是不可压缩的;在加密之前压缩明文会导致信息泄漏(例如犯罪和破坏TLS)。
确保你用
mb_strlen()和
mb_substr(),使用
‘8bit’字符集模式以防止
mbstring.func_overload问题。
IVs应该使用如果你正在使用
mcrypt_create_iv(), 不使用MCRYPT_RAND!也要退房
除非你用的是AIAD结构,
bin2hex(), base64_encode()等等,可能会通过缓存定时泄露关于加密密钥的信息。如果可能的话,尽量避开他们。
即使你遵循这里给出的建议,密码学也会有很多问题。请密码专家检查您的实现。如果你没有足够的幸运成为当地大学密码学生的私人朋友,你可以尝试密码栈交换咨询论坛。
如果您需要对您的实现进行专业分析,则可以随时雇用著名的安全顾问小组来检查您的PHP加密代码(披露:我的雇主)
注意:何时不使用加密?
..你想散列相反,使用这些密码哈希算法之一:
不要使用通用哈希函数(MD5,SHA 256)来存储密码.
这是做这项工作的错误工具。
使用LibNa的PHP字符串加密示例
如果您使用的是PHP<7.2或其他情况下没有安装libNa,则可以使用钠复配完成相同的结果(尽管速度较慢)。<?php
declare(strict_types=1);/**
* Encrypt a message
*
* @param string $message – message to encrypt
* @param string $key – encryption key
* @return string
* @throws RangeException
*/function safeEncrypt(string $message, string $key): string{
if (mb_strlen($key, ‘8bit’) !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
throw new RangeException(‘Key is not the correct size (must be 32 bytes).’);
}
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$cipher = base64_encode(
$nonce.
sodium_crypto_secretbox(
$message,
$nonce,
$key )
);
sodium_memzero($message);
sodium_memzero($key);
return $cipher;}/**
* Decrypt a message
*
* @param string $encrypted – message encrypted with safeEncrypt()
* @param string $key – encryption key
* @return string
* @throws Exception
*/function safeDecrypt(string $encrypted, string $key): string{
$decoded = base64_decode($encrypted);
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, ‘8bit’);
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, ‘8bit’);
$plain = sodium_crypto_secretbox_open(
$ciphertext,
$nonce,
$key );
if (!is_string($plain)) {
throw new Exception(‘Invalid MAC’);
}
sodium_memzero($ciphertext);
sodium_memzero($key);
return $plain;}
然后测试一下:<?php // This refers to the previous code block.require “safeCrypto.php”; // Do this once then store it somehow:$key = random_bytes(SODIUM_
CRYPTO_SECRETBOX_KEYBYTES);$message = ‘We are all living in a yellow submarine’;$ciphertext = safeEncrypt($message, $key);$plaintext =
safeDecrypt($ciphertext, $key);var_dump($ciphertext);var_dump($plaintext);
哈利特-利比钠
我一直在做的一个项目是一个名为卤石,它的目的是使锂钠更容易,更直观。<?phpuse \ParagonIE\Halite\KeyFactory;use \ParagonIE\Halite\Symmetric\Crypto as SymmetricCrypto;// Generate a new random symmetric-key
encryption key. You’re going to want to store this:$key = new KeyFactory::generateEncryptionKey();// To save your encryption key:KeyFact
ory::save($key, ‘/path/to/secret.key’);// To load it again:$loadedkey = KeyFactory::loadEncryptionKey(‘/path/to/secret.key’);$message =
‘We are all living in a yellow submarine’;$ciphertext = SymmetricCrypto::encrypt($message, $key);$plaintext = SymmetricCrypto::decrypt($
ciphertext, $key);var_dump($ciphertext);var_dump($plaintext);
所有的底层密码学都是由libNa处理的。
使用Defuse/php加密的示例<?php /**
* This requires https://github.com/defuse/php-encryption
* php composer.phar require defuse/php-encryption
*/use Defuse\Crypto\Crypto;use Defuse\Crypto\Key;require “vendor/autoload.php”;// Do this once then store it somehow:$key = Key:
:createNewRandomKey();$message = ‘We are all living in a yellow submarine’;$ciphertext = Crypto::encrypt($message, $key);$plaintext = Crypto:
:decrypt($ciphertext, $key);var_dump($ciphertext);var_dump($plaintext);
注: Crypto::encrypt()返回十六进制编码的输出。
加密密钥管理
如果你想使用“密码”,现在就停止。你需要一个随机的128位加密密钥,而不是一个人类难忘的密码.
您可以存储用于长期使用的加密密钥,如下所示:$storeMe = bin2hex($key);
而且,根据需要,您可以这样检索它:$key = hex2bin($storeMe);
I 强建议只存储一个随机生成的密钥供长期使用,而不是任何类型的密码作为密钥(或派生密钥)。
如果您正在使用的库:
“但是我真的想要使用密码。“
这是个坏主意,但好吧,这是安全的方法。
首先,生成一个随机密钥并将其存储在一个常量中。/**
* Replace this with your own salt!
* Use bin2hex() then add \x before every 2 hex characters, like so:
*/define(‘MY_PBKDF2_SALT’, “\x2d\xb7\x68\x1a\x28\x15\xbe\x06\x33\xa0\x7e\x0e\x8f\x79\xd5\xdf”);
请注意,您正在添加额外的工作,只需使用这个常量作为关键,并为自己节省了很多心痛!
然后使用PBKDF 2(类似)从您的密码中派生出一个合适的加密密钥,而不是直接使用您的密码进行加密。/**
* Get an AES key from a static password and a secret salt
*
* @param string $password Your weak password here
* @param int $keysize Number of bytes in encryption key
*/function getKeyFromPassword($password, $keysize = 16){
return hash_pbkdf2(
‘sha256’,
$password,
MY_PBKDF2_SALT,
100000, // Number of iterations
$keysize,
true
);}
不要只使用16个字符的密码。你的加密密钥会被破坏。