如何加密和解密一个PHP字符串?

我的意思是:

Original String + Salt or Key --> Encrypted String
Encrypted String + Salt or Key --> Decrypted (Original String)

也许是这样的:

"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"
  • 在PHP中,如何做到这一点?

尝试使用Crypt_Blowfish,但它不为我工作。

582774 次浏览

历史的注意:这是在PHP4时代编写的。这就是我们现在所说的“遗留代码”。

出于历史原因,我留下了这个答案——但有些方法现在已弃用,DES加密方法不是推荐的实践,等等。

我没有更新这段代码有两个原因:1)我不再在PHP中手工使用加密方法,2)这段代码仍然达到了预期的目的:演示如何在PHP中进行加密的最简单的概念。

如果你发现类似简单的“PHP加密傻瓜”的源代码,可以让人们在10-20行代码或更少的代码中开始,请在评论中告诉我。

除此之外,请欣赏这个早期PHP4极简加密答案的经典集。


理想情况下,您已经或能够访问mcrypt PHP库,因为它确实很受欢迎,对各种任务非常有用。下面是不同类型的加密和一些示例代码:PHP加密技术

//Listing 3: Encrypting Data Using the mcrypt_ecb Function


<?php
echo("<h3> Symmetric Encryption </h3>");
$key_value = "KEYVALUE";
$plain_text = "PLAINTEXT";
$encrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $plain_text, MCRYPT_ENCRYPT);
echo ("<p><b> Text after encryption : </b>");
echo ( $encrypted_text );
$decrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $encrypted_text, MCRYPT_DECRYPT);
echo ("<p><b> Text after decryption : </b>");
echo ( $decrypted_text );
?>

一些警告:

1)当单向哈希可以使用时,永远不要使用可逆或“对称”加密。

2)如果数据非常敏感,如信用卡或社会安全号码,停止;您需要的不仅仅是任何简单的代码块所能提供的,而是您需要为此目的设计的加密库,以及大量的时间来研究所需的方法。此外,软件加密可能是敏感数据安全性的10%。这就像给核电站重新布线一样——如果是这样的话,接受这个任务是危险和困难的,而且超出了你的知识范围。财务惩罚可能是巨大的,所以最好使用服务并将责任交付给他们。

3)任何一种容易实现的加密,如这里所列,都可以合理地保护你想要避免被窥探或限制意外/故意泄露的情况下暴露的轻度重要信息。但鉴于密钥是如何以纯文本形式存储在web服务器上的,如果他们能获得数据,他们就能获得解密密钥。

尽管如此,玩得开心吧。

不要做什么

< p > < >强警告:< br > 这个答案使用欧洲央行。ECB不是一个加密模式,它只是一个构建模块。使用这个答案中演示的ECB实际上并不能安全地加密字符串。不要在代码中使用ECB。

. b

. b

我自己弄到的。实际上我在谷歌上找到了一些答案,只是修改了一些东西。然而,结果是完全不安全的。

<?php
define("ENCRYPTION_KEY", "!@#$%^&*");
$string = "This is the original data string!";


echo $encrypted = encrypt($string, ENCRYPTION_KEY);
echo "<br />";
echo $decrypted = decrypt($encrypted, ENCRYPTION_KEY);


/**
* Returns an encrypted & utf8-encoded
*/
function encrypt($pure_string, $encryption_key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
return $encrypted_string;
}


/**
* Returns decrypted original string
*/
function decrypt($encrypted_string, $encryption_key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
return $decrypted_string;
}
?>

在你做任何事情之前,试着理解<强>加密和<强>认证的区别,以及为什么你可能想要经过身份验证的加密而不仅仅是加密

要实现经过身份验证的加密,你需要加密然后MAC. 加密和身份验证的顺序非常重要!这个问题的现有答案之一犯了这个错误;就像许多用PHP编写的密码库一样。

你应该避免实现自己的密码学,而不是使用一个由密码学专家编写和审查的安全库。

更新:PHP 7.2现在提供libsodium !为了获得最好的安全性,请将您的系统更新为使用PHP 7.2或更高版本,并且只遵循本文中的libsodium建议。

< p > 使用libsodium如果你有PECL访问(或sodium compat如果你想要libsodium没有PECL);否则……
使用缓和/ php-encryption;不要滚你自己的密码!< / >强

上面链接的两个库使您可以轻松地在自己的库中实现经过身份验证的加密。

如果您仍然想编写和部署您自己的密码学库,与互联网上每个密码学专家的传统智慧相反,那么您必须采取以下步骤。

加密:

  1. CTR模式下使用AES加密。您也可以使用GCM(它消除了对单独MAC的需要)。此外,ChaCha20和Salsa20(由libsodium提供)是流密码,不需要特殊模式。
  2. 除非您选择了上面的GCM,否则您应该使用HMAC-SHA-256(或者,对于流密码,使用Poly1305—大多数libsodium api为您做这件事)来验证密文。MAC应该能覆盖IV和密文!

解密:

  1. 除非使用Poly1305或GCM,否则重新计算密文的MAC,并将其与使用hash_equals()发送的MAC进行比较。如果失败,中止。
  2. 解密消息。

其他设计考虑因素:

  1. 不要压缩任何东西。密文不可压缩;在加密之前压缩明文会导致信息泄露(例如TLS上的CRIME和BREACH)。
  2. 确保你使用mb_strlen()mb_substr(),使用'8bit'字符集模式来防止mbstring.func_overload问题。
  3. IVs应该使用CSPRNG生成;如果你正在使用mcrypt_create_iv()不要使用__abc1!
  4. 除非你使用的是AEAD结构,ALWAYS encrypt then MAC!< / >强
  5. bin2hex()base64_encode()等可能会通过缓存计时泄露有关加密密钥的信息。尽可能避免使用。

即使您遵循这里给出的建议,密码学也会出现很多问题。始终让密码学专家检查您的实现。如果你没有足够的幸运与你当地大学的密码学学生成为朋友,你可以尝试密码堆栈交换论坛寻求建议。

如果你需要对你的实现进行专业的分析,你总是可以雇佣一个由信誉良好的安全顾问团队审查您的PHP加密代码(披露:我的雇主)。

重要提示:何时不使用加密

Don't encrypt passwords .。你想要哈希它们,而是使用以下密码哈希算法之一:

永远不要使用通用哈希函数(MD5, SHA256)来存储密码。

这个工具不适合做这项工作。

使用Libsodium的PHP字符串加密示例

如果你使用PHP <7.2或其他没有安装libsodium的情况下,你可以使用sodium_compat来完成相同的结果(尽管速度较慢)。

<?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);

Halite - Libsodium更容易

我一直在做的一个项目是一个名为岩盐的加密库,它旨在使libsodium更简单、更直观。

<?php
use \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:
KeyFactory::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);

所有底层密码学都由libsodium处理。

使用化解/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);

强烈建议只是存储一个随机生成的密钥长期使用,而不是任何类型的密码作为密钥(或派生密钥)。

如果你正在使用化解的库:

“但是我真的想要一个密码。”

这是个坏主意,但好吧,下面是安全的方法。

首先,生成一个随机键并将其存储在一个常量中。

/**
* 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");

请注意,您正在增加额外的工作,可以使用这个常量作为关键,从而为自己省去很多麻烦!

然后使用PBKDF2(像这样)从您的密码派生一个合适的加密密钥,而不是直接使用您的密码进行加密。

/**
* 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个字符的密码。你的加密密钥会被滑稽地破解。

对于Laravel框架

如果您正在使用Laravel框架,那么使用内部函数加密和解密将更加容易。

$string = 'Some text to be encrypted';
$encrypted = \Illuminate\Support\Facades\Crypt::encrypt($string);
$decrypted_string = \Illuminate\Support\Facades\Crypt::decrypt($encrypted);


var_dump($string);
var_dump($encrypted);
var_dump($decrypted_string);

注意:请确保在文件中设置一个16,24或32个字符的随机字符串 config/app.php文件的关键选项。否则,加密值

我迟到了,但在寻找正确的方法时,我看到了这个页面,它是谷歌搜索结果最高的页面之一,所以我想分享我对这个问题的看法,我认为在写这篇文章时(2017年初)它是最新的。从PHP 7.1.0开始,mcrypt_decryptmcrypt_encrypt将被弃用,因此构建未来的证明代码应该使用openssl_encryptopenssl_decrypt

你可以这样做:

$string_to_encrypt="Test";
$password="password";
$encrypted_string=openssl_encrypt($string_to_encrypt,"AES-128-ECB",$password);
$decrypted_string=openssl_decrypt($encrypted_string,"AES-128-ECB",$password);

重要的:使用欧洲央行的模式,这是不安全的。如果你想要一个简单的解决方案,而不需要参加密码学工程的速成班,不要自己编写,只需使用一个库。

您也可以使用任何其他芯片方法,这取决于您的安全需求。要找出可用的chipper方法,请参阅openssl_get_cipher_methods函数。

如果你不想使用库(你应该),那么使用这样的东西(PHP 7):

function sign($message, $key) {
return hash_hmac('sha256', $message, $key) . $message;
}


function verify($bundle, $key) {
return hash_equals(
hash_hmac('sha256', mb_substr($bundle, 64, null, '8bit'), $key),
mb_substr($bundle, 0, 64, '8bit')
);
}


function getKey($password, $keysize = 16) {
return hash_pbkdf2('sha256',$password,'some_token',100000,$keysize,true);
}


function encrypt($message, $password) {
$iv = random_bytes(16);
$key = getKey($password);
$result = sign(openssl_encrypt($message,'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv), $key);
return bin2hex($iv).bin2hex($result);
}


function decrypt($hash, $password) {
$iv = hex2bin(substr($hash, 0, 32));
$data = hex2bin(substr($hash, 32));
$key = getKey($password);
if (!verify($data, $key)) {
return null;
}
return openssl_decrypt(mb_substr($data, 64, null, '8bit'),'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv);
}


$string_to_encrypt='John Smith';
$password='password';
$encrypted_string=encrypt($string_to_encrypt, $password);
$decrypted_string=decrypt($encrypted_string, $password);

下面的代码适用于php中所有具有特殊字符的字符串

   // Encrypt text --


$token = "9611222007552";


$cipher_method = 'aes-128-ctr';
$enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);
$enc_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method));
$crypted_token = openssl_encrypt($token, $cipher_method, $enc_key, 0, $enc_iv) . "::" . bin2hex($enc_iv);
echo    $crypted_token;
//unset($token, $cipher_method, $enc_key, $enc_iv);


// Decrypt text  --


list($crypted_token, $enc_iv) = explode("::", $crypted_token);
$cipher_method = 'aes-128-ctr';
$enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);
$token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv));
echo   $token;
//unset($crypted_token, $cipher_method, $enc_key, $enc_iv);

更新

PHP 7就绪版本。它使用了PHP OpenSSL库中的openssl_encrypt函数。

class Openssl_EncryptDecrypt {
function encrypt ($pure_string, $encryption_key) {
$cipher     = 'AES-256-CBC';
$options    = OPENSSL_RAW_DATA;
$hash_algo  = 'sha256';
$sha2len    = 32;
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($pure_string, $cipher, $encryption_key, $options, $iv);
$hmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
return $iv.$hmac.$ciphertext_raw;
}
function decrypt ($encrypted_string, $encryption_key) {
$cipher     = 'AES-256-CBC';
$options    = OPENSSL_RAW_DATA;
$hash_algo  = 'sha256';
$sha2len    = 32;
$ivlen = openssl_cipher_iv_length($cipher);
$iv = substr($encrypted_string, 0, $ivlen);
$hmac = substr($encrypted_string, $ivlen, $sha2len);
$ciphertext_raw = substr($encrypted_string, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $encryption_key, $options, $iv);
$calcmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
if(function_exists('hash_equals')) {
if (hash_equals($hmac, $calcmac)) return $original_plaintext;
} else {
if ($this->hash_equals_custom($hmac, $calcmac)) return $original_plaintext;
}
}
/**
* (Optional)
* hash_equals() function polyfilling.
* PHP 5.6+ timing attack safe comparison
*/
function hash_equals_custom($knownString, $userString) {
if (function_exists('mb_strlen')) {
$kLen = mb_strlen($knownString, '8bit');
$uLen = mb_strlen($userString, '8bit');
} else {
$kLen = strlen($knownString);
$uLen = strlen($userString);
}
if ($kLen !== $uLen) {
return false;
}
$result = 0;
for ($i = 0; $i < $kLen; $i++) {
$result |= (ord($knownString[$i]) ^ ord($userString[$i]));
}
return 0 === $result;
}
}


define('ENCRYPTION_KEY', '__^%&Q@$&*!@#$%^&*^__');
$string = "This is the original string!";


$OpensslEncryption = new Openssl_EncryptDecrypt;
$encrypted = $OpensslEncryption->encrypt($string, ENCRYPTION_KEY);
$decrypted = $OpensslEncryption->decrypt($encrypted, ENCRYPTION_KEY);

这些是使用AES256 CBC在PHP中加密/解密字符串的紧凑方法:

function encryptString($plaintext, $password, $encoding = null) {
$iv = openssl_random_pseudo_bytes(16);
$ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext.$iv, hash('sha256', $password, true), true);
return $encoding == "hex" ? bin2hex($iv.$hmac.$ciphertext) : ($encoding == "base64" ? base64_encode($iv.$hmac.$ciphertext) : $iv.$hmac.$ciphertext);
}


function decryptString($ciphertext, $password, $encoding = null) {
$ciphertext = $encoding == "hex" ? hex2bin($ciphertext) : ($encoding == "base64" ? base64_decode($ciphertext) : $ciphertext);
if (!hash_equals(hash_hmac('sha256', substr($ciphertext, 48).substr($ciphertext, 0, 16), hash('sha256', $password, true), true), substr($ciphertext, 16, 32))) return null;
return openssl_decrypt(substr($ciphertext, 48), "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, substr($ciphertext, 0, 16));
}

用法:

$enc = encryptString("mysecretText", "myPassword");
$dec = decryptString($enc, "myPassword");

编辑:这是一个新版本的函数,使用AES256 GCMPBKDF2作为密钥派生,更安全。

function str_encryptaesgcm($plaintext, $password, $encoding = null) {
if ($plaintext != null && $password != null) {
$keysalt = openssl_random_pseudo_bytes(16);
$key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-gcm"));
$tag = "";
$encryptedstring = openssl_encrypt($plaintext, "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag, "", 16);
return $encoding == "hex" ? bin2hex($keysalt.$iv.$encryptedstring.$tag) : ($encoding == "base64" ? base64_encode($keysalt.$iv.$encryptedstring.$tag) : $keysalt.$iv.$encryptedstring.$tag);
}
}


function str_decryptaesgcm($encryptedstring, $password, $encoding = null) {
if ($encryptedstring != null && $password != null) {
$encryptedstring = $encoding == "hex" ? hex2bin($encryptedstring) : ($encoding == "base64" ? base64_decode($encryptedstring) : $encryptedstring);
$keysalt = substr($encryptedstring, 0, 16);
$key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
$ivlength = openssl_cipher_iv_length("aes-256-gcm");
$iv = substr($encryptedstring, 16, $ivlength);
$tag = substr($encryptedstring, -16);
return openssl_decrypt(substr($encryptedstring, 16 + $ivlength, -16), "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag);
}
}

用法:

$enc = str_encryptaesgcm("mysecretText", "myPassword", "base64"); // return a base64 encrypted string, you can also choose hex or null as encoding.
$dec = str_decryptaesgcm($enc, "myPassword", "base64");

在PHP中,可以使用一种名为OpenSSL的加密扩展函数对字符串进行加密和解密。

openssl_encrypt()函数:openssl_encrypt()函数用于加密数据。

语法如下:

openssl_encrypt(字符串$data,字符串$method,字符串$key, $options = 0,字符串$iv,字符串$tag= NULL,字符串$aad, int $tag_length = 16)

参数说明如下:

元数据:保存需要加密的字符串或数据。

方法:美元 cipher方法使用openssl_get_cipher_methods()函数。

关键:美元它持有加密密钥。

美元的选择:它保存了标记OPENSSL_RAW_DATA和OPENSSL_ZERO_PADDING的位解取。

四:美元它保存的初始化向量不是NULL。

标签:美元它保存在使用AEAD密码模式(GCM或CCM)时通过引用传递的身份验证标记。

美元的广告:它保存额外的身份验证数据。

$ tag_length:它保存身份验证标记的长度。对于GCM模式,认证标签的长度在4到16之间。

返回值:成功时返回加密字符串,失败时返回FALSE。

openssl_decrypt()函数openssl_decrypt()用于解密数据。

语法如下:

openssl_decrypt(字符串$data,字符串$method,字符串$key, int $options = 0,字符串$iv,字符串$tag,字符串$aad)

参数说明如下:

元数据:保存需要加密的字符串或数据。

方法:美元 cipher方法使用openssl_get_cipher_methods()函数。

关键:美元它持有加密密钥。

美元的选择:它保存了标记OPENSSL_RAW_DATA和OPENSSL_ZERO_PADDING的位解取。

四:美元它保存的初始化向量不是NULL。

标签:美元它保存使用AEAD密码模式(GCM或CCM)的身份验证标记。当身份验证失败时,openssl_decryption()返回FALSE。

美元的广告:它保存额外的身份验证数据。

返回值:成功时返回解密字符串,失败时返回FALSE。

方法:首先声明一个字符串并将其存储到变量中,然后使用openssl_encrypt()函数对给定的字符串进行加密,使用openssl_decrypt()函数对给定的字符串进行解密。

你可以在以下网址找到相关例子: https://www.geeksforgeeks.org/how-to-encrypt-and-decrypt-a-php-string/