如何对PHP字符串进行加密和解密?

时间:2021-10-08 18:28:41

What I mean is:

我的意思是:

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

Maybe something like:

也许类似:

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

Attempted to use Crypt_Blowfish, but it didn't work for me.

尝试使用Crypt_Blowfish,但它对我不起作用。

5 个解决方案

#1


306  

Before you do anything further, seek to understand the difference between encryption and authentication, and why you probably want authenticated encryption rather than just encryption.

在您进一步做任何事情之前,请尝试理解加密和身份验证之间的区别,以及为什么您可能需要经过身份验证的加密,而不只是加密。

To implement authenticated encryption, you want to Encrypt then MAC. The order of encryption and authentication is very important! One of the existing answers to this question made this mistake; as do many cryptography libraries written in PHP.

要实现经过身份验证的加密,您需要对MAC进行加密。加密和身份验证的顺序非常重要!对这个问题已有的回答中有一个犯了这个错误;和许多用PHP编写的加密库一样。

You should avoid implementing your own cryptography, and instead use a secure library written by and reviewed by cryptography experts.

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

Update: PHP 7.2 now provides libsodium! Updated to PHP 7.2 or higher and only follow the libsodium advice in this answer.

更新:PHP 7.2现在提供lib钠!更新到PHP 7.2或更高,并且只遵循lib钠的建议在这个答案。

Use libsodium if you have PECL access (or sodium_compat if you want libsodium without PECL); otherwise...
Use defuse/php-encryption; don't roll your own cryptography!

如果你有PECL(或者sodium_compat,如果你想要没有PECL的lib钠),就使用lib钠;否则……使用缓和/ php-encryption;不要滚动你自己的密码学!

Both of the libraries linked above above make it easy and painless to implement authenticated encryption into your own libraries.

上述两种库都可以轻松轻松地将经过身份验证的加密实现到自己的库中。

If you still want to write and deploy your own cryptography library, against the conventional wisdom of every cryptography expert on the Internet, these are the steps you would have to take.

如果您仍然希望编写和部署自己的加密库,与Internet上的所有加密专家的传统智慧相违背,那么您将不得不采取这些步骤。

Encryption:

  1. Encrypt using AES in CTR mode. You may also use GCM (which removes the need for a separate MAC). Additionally, ChaCha20 and Salsa20 (provided by libsodium) are stream ciphers and do not need special modes.
  2. 在CTR模式下使用AES进行加密。您还可以使用GCM(它消除了单独的MAC的需要)。另外,ChaCha20和Salsa20(由libna提供)是流密码,不需要特殊的模式。
  3. Unless you chose GCM above, you should authenticate the ciphertext with HMAC-SHA-256 (or, for the stream ciphers, Poly1305 -- most libsodium APIs do this for you). The MAC should cover the IV as well as the ciphertext!
  4. 除非您选择上面的GCM,否则您应该使用hmc - sha -256(或者,对于流密码,Poly1305—大多数libna api为您这样做)来认证密文。MAC应该包括IV和密文!

Decryption:

  1. Unless Poly1305 or GCM is used, recalculate the MAC of the ciphertext and compare it with the MAC that was sent using hash_equals(). If it fails, abort.
  2. 除非使用Poly1305或GCM,重新计算密文的MAC,并将其与使用hash_equals()发送的MAC进行比较。如果失败了,中止。
  3. Decrypt the message.
  4. 解密消息。

Other Design Considerations:

  1. Do not compress anything ever. Ciphertext is not compressible; compressing plaintext before encryption can lead to information leaks (e.g. CRIME and BREACH on TLS).
  2. 不要压缩任何东西。密文不是可压缩;在加密前压缩明文可以导致信息泄露(例如:犯罪和对TLS的破坏)。
  3. Make sure you use mb_strlen() and mb_substr(), using the '8bit' character set mode to prevent mbstring.func_overload issues.
  4. 确保使用mb_strlen()和mb_substr(),使用“8bit”字符集模式来防止mbstring。func_overload问题。
  5. IVs should be generating using a CSPRNG; If you're using mcrypt_create_iv(), DO NOT USE MCRYPT_RAND!
  6. IVs应该使用CSPRNG生成;如果您使用的是mcrypt_create_iv(),请不要使用MCRYPT_RAND!也看看random_compat。
  7. Unless you're using an AEAD construct, ALWAYS encrypt then MAC!
  8. 除非你使用的是AEAD结构,否则总是加密MAC!
  9. bin2hex(), base64_encode(), etc. may leak information about your encryption keys via cache timing. Avoid them if possible.
  10. bin2hex()、base64_encode()等可能通过缓存定时泄漏有关您的加密密钥的信息。尽可能避免他们。

Even if you follow the advice given here, a lot can go wrong with cryptography. Always have a cryptography expert review your implementation. If you are not fortunate enough to be personal friends with a cryptography student at your local university, you can always try the Cryptography Stack Exchange forum for advice.

即使你遵循这里给出的建议,密码术也会出错。始终要有一个密码专家检查您的实现。如果你没有足够的幸运成为当地大学的密码学学生的私人朋友,你可以尝试一下密码学栈交流论坛。

If you need a professional analysis of your implementation, you can always hire a reputable team of security consultants to review your PHP cryptography code (disclosure: my employer).

如果您需要对您的实现进行专业的分析,您可以一直雇佣一个有信誉的安全顾问团队来检查您的PHP密码代码(披露:我的雇主)。

Important: When to Not Use Encryption

Don't encrypt passwords. You want to hash them instead, using one of these password-hashing algorithms:

不加密的密码。你想用一个密码散列算法来哈希它们,

Never use a general-purpose hash function (MD5, SHA256) for password storage.

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

Don't encrypt URL Parameters. It's the wrong tool for the job.

不加密URL参数。这是这个工作的错误工具。

PHP String Encryption Example with Libsodium

If you are on PHP < 7.2 or otherwise do not have libsodium installed, you can use sodium_compat to accomplish the same result (albeit slower).

如果您在PHP < 7.2或其他情况下没有安装lib钠,您可以使用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 he 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;
}

Then to test it out:

然后测试一下:

<?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 Made Easier

One of the projects I've been working on is an encryption library called Halite, which aims to make libsodium easier and more intuitive.

我一直在做的一个项目是一个名为Halite的加密库,它的目的是使lib钠更容易、更直观。

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

All of the underlying cryptography is handled by libsodium.

所有底层密码学都是由libna处理的。

Example with defuse/php-encryption

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

Note: Crypto::encrypt() returns hex-encoded output.

加密()返回十六进制编码的输出。

Encryption Key Management

If you're tempted to use a "password", stop right now. You need a random 128-bit encryption key, not a human memorable password.

如果你想使用“密码”,现在就停止。你需要一个随机的128位加密密钥,而不是一个令人难忘的密码。

You can store an encryption key for long-term use like so:

您可以存储加密密钥,以便长期使用:

$storeMe = bin2hex($key);

And, on demand, you can retrieve it like so:

根据需求,你可以这样检索:

$key = hex2bin($storeMe);

I strongly recommend just storing a randomly generated key for long-term use instead of any sort of password as the key (or to derive the key).

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

If you're using Defuse's library:

如果你使用的是拆除的图书馆:

"But I really want to use a password."

That's a bad idea, but okay, here's how to do it safely.

这是个坏主意,但好吧,下面是如何安全地做这件事。

First, generate a random key and store it in a constant.

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

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

Note that you're adding extra work and could just use this constant as the key and save yourself a lot of heartache!

请注意,您正在添加额外的工作,并且可以使用这个常量作为键,并为自己节省大量的心痛!

Then use PBKDF2 (like so) to derive a suitable encryption key from your password rather than encrypting with your password directly.

然后使用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
    );
}

Don't just use a 16-character password. Your encryption key will be comically broken.

不要只使用16字符的密码。你的加密密钥将被粉碎。

#2


43  

What not to do

不该做什么

WARNING:
This answer uses ECB. ECB is not an encryption mode, it's only a building block. Using ECB as demonstrated in this answer does not actually encrypt the string securely. Do not use ECB in your code. See Scott's answer for a good solution.

警告:这个答案使用欧洲央行。欧洲央行不是加密模式,它只是一个构建块。在这个答案中使用欧洲央行实际上并没有对字符串加密。不要在代码中使用欧洲央行。看看斯科特给出的解决方案。

I got it on myself. Actually i found some answer on google and just modified something. The result is completely insecure however.

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

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

#3


18  

I'm late to the party, but searching for the correct way to do it I came across this page it was one of the top Google search returns, so I will like to share my view on the problem, which I consider it to be up to date at the time of writing this post (beginning of 2017). From PHP 7.1.0 the mcrypt_decrypt and mcrypt_encrypt is going to be deprecated, so building future proof code should use openssl_encrypt and openssl_decrypt

我迟到了,但寻找正确的方法我偶然发现这个页面是数一数二的谷歌搜索返回,所以我将分享我对这个问题的看法,我认为这是最新的写这篇文章的时候(2017年开始)。从PHP 7.1.0中,mcrypt_decrypt和mcrypt_encrypt将被弃用,因此构建未来的证明代码应该使用openssl_encrypt和openssl_decrypt。

You can do something like:

你可以这样做:

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

Important: This uses ECB mode, which isn't secure. If you want a simple solution without taking a crash course in cryptography engineering, don't write it yourself, just use a library.

重要:这使用了欧洲央行的模式,这是不安全的。如果您想要一个简单的解决方案,而不需要在加密工程中使用速成课程,请不要自己编写,只需使用一个库即可。

You can use any other chipper methods as well, depending on your security need. To find out the available chipper methods please see the openssl_get_cipher_methods function.

您还可以使用任何其他的chipper方法,这取决于您的安全需求。要找到可用的chipper方法,请查看openssl_get_cipher_methods函数。

#4


8  

For Laravel framework

If you are using Laravel framework then it's more easy to encrypt and decrypt with internal functions.

如果您使用的是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);

Note: Be sure to set a 16, 24, or 32 character random string in the key option of the config/app.php file. Otherwise, encrypted values will not be secure.

注意:确保在配置/应用程序的关键选项中设置16、24或32字符的随机字符串。php文件。否则,加密的值将不安全。

#5


6  

Historical Note: This was written at the time of PHP4. This is what we call "legacy code" now.

历史记录:这是在PHP4的时候写的。这就是我们现在所说的“遗留代码”。

I have left this answer for historical purposes - but some of the methods are now deprecated, DES encryption method is not a recommended practice, etc.

我已经将这个答案留给了历史目的——但是有些方法现在已经过时了,DES加密方法不是推荐的做法,等等。

I have not updated this code for two reasons: 1) I no longer work with encryption methods by hand in PHP, and 2) this code still serves the purpose it was intended for: to demonstrate the minimum, simplistic concept of how encryption can work in PHP.

我没有更新这段代码有两个原因:1)我不再使用PHP的加密方法,2)这段代码仍然符合它的目的:演示加密如何在PHP中工作的最小的、简单的概念。

If you find a similarly simplistic, "PHP encryption for dummies" kind of source that can get people started in 10-20 lines of code or less, let me know in comments.

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

Beyond that, please enjoy this Classic Episode of early-era PHP4 minimalistic encryption answer.

除此之外,请欣赏这一经典的早期PHP4简约加密回答。


Ideally you have - or can get - access to the mcrypt PHP library, as its certainly popular and very useful a variety of tasks. Here's a run down of the different kinds of encryption and some example code: Encryption Techniques in PHP

理想情况下,您可以访问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 ); 
?> 

A few warnings:

一些警告:

1) Never use reversible, or "symmetric" encryption when a one-way hash will do.

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

2) If the data is truly sensitive, like credit card or social security numbers, stop; you need more than any simple chunk of code will provide, but rather you need a crypto library designed for this purpose and a significant amount of time to research the methods necessary. Further, the software crypto is probably <10% of security of sensitive data. It's like rewiring a nuclear power station - accept that the task is dangerous and difficult and beyond your knowledge if that's the case. The financial penalties can be immense, so better to use a service and ship responsibility to them.

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

3) Any sort of easily implementable encryption, as listed here, can reasonably protect mildly important information that you want to keep from prying eyes or limit exposure in the case of accidental/intentional leak. But seeing as how the key is stored in plain text on the web server, if they can get the data they can get the decryption key.

3)任何一种易于实现的加密,如这里所列,可以合理地保护您想要避免窥视的重要信息,或者在意外/故意泄漏的情况下限制暴露。但是,如果将密钥存储在web服务器上的纯文本中,如果它们能够获得数据,就可以获得解密密钥。

Be that as it may, have fun :)

尽管如此,祝你愉快:)

#1


306  

Before you do anything further, seek to understand the difference between encryption and authentication, and why you probably want authenticated encryption rather than just encryption.

在您进一步做任何事情之前,请尝试理解加密和身份验证之间的区别,以及为什么您可能需要经过身份验证的加密,而不只是加密。

To implement authenticated encryption, you want to Encrypt then MAC. The order of encryption and authentication is very important! One of the existing answers to this question made this mistake; as do many cryptography libraries written in PHP.

要实现经过身份验证的加密,您需要对MAC进行加密。加密和身份验证的顺序非常重要!对这个问题已有的回答中有一个犯了这个错误;和许多用PHP编写的加密库一样。

You should avoid implementing your own cryptography, and instead use a secure library written by and reviewed by cryptography experts.

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

Update: PHP 7.2 now provides libsodium! Updated to PHP 7.2 or higher and only follow the libsodium advice in this answer.

更新:PHP 7.2现在提供lib钠!更新到PHP 7.2或更高,并且只遵循lib钠的建议在这个答案。

Use libsodium if you have PECL access (or sodium_compat if you want libsodium without PECL); otherwise...
Use defuse/php-encryption; don't roll your own cryptography!

如果你有PECL(或者sodium_compat,如果你想要没有PECL的lib钠),就使用lib钠;否则……使用缓和/ php-encryption;不要滚动你自己的密码学!

Both of the libraries linked above above make it easy and painless to implement authenticated encryption into your own libraries.

上述两种库都可以轻松轻松地将经过身份验证的加密实现到自己的库中。

If you still want to write and deploy your own cryptography library, against the conventional wisdom of every cryptography expert on the Internet, these are the steps you would have to take.

如果您仍然希望编写和部署自己的加密库,与Internet上的所有加密专家的传统智慧相违背,那么您将不得不采取这些步骤。

Encryption:

  1. Encrypt using AES in CTR mode. You may also use GCM (which removes the need for a separate MAC). Additionally, ChaCha20 and Salsa20 (provided by libsodium) are stream ciphers and do not need special modes.
  2. 在CTR模式下使用AES进行加密。您还可以使用GCM(它消除了单独的MAC的需要)。另外,ChaCha20和Salsa20(由libna提供)是流密码,不需要特殊的模式。
  3. Unless you chose GCM above, you should authenticate the ciphertext with HMAC-SHA-256 (or, for the stream ciphers, Poly1305 -- most libsodium APIs do this for you). The MAC should cover the IV as well as the ciphertext!
  4. 除非您选择上面的GCM,否则您应该使用hmc - sha -256(或者,对于流密码,Poly1305—大多数libna api为您这样做)来认证密文。MAC应该包括IV和密文!

Decryption:

  1. Unless Poly1305 or GCM is used, recalculate the MAC of the ciphertext and compare it with the MAC that was sent using hash_equals(). If it fails, abort.
  2. 除非使用Poly1305或GCM,重新计算密文的MAC,并将其与使用hash_equals()发送的MAC进行比较。如果失败了,中止。
  3. Decrypt the message.
  4. 解密消息。

Other Design Considerations:

  1. Do not compress anything ever. Ciphertext is not compressible; compressing plaintext before encryption can lead to information leaks (e.g. CRIME and BREACH on TLS).
  2. 不要压缩任何东西。密文不是可压缩;在加密前压缩明文可以导致信息泄露(例如:犯罪和对TLS的破坏)。
  3. Make sure you use mb_strlen() and mb_substr(), using the '8bit' character set mode to prevent mbstring.func_overload issues.
  4. 确保使用mb_strlen()和mb_substr(),使用“8bit”字符集模式来防止mbstring。func_overload问题。
  5. IVs should be generating using a CSPRNG; If you're using mcrypt_create_iv(), DO NOT USE MCRYPT_RAND!
  6. IVs应该使用CSPRNG生成;如果您使用的是mcrypt_create_iv(),请不要使用MCRYPT_RAND!也看看random_compat。
  7. Unless you're using an AEAD construct, ALWAYS encrypt then MAC!
  8. 除非你使用的是AEAD结构,否则总是加密MAC!
  9. bin2hex(), base64_encode(), etc. may leak information about your encryption keys via cache timing. Avoid them if possible.
  10. bin2hex()、base64_encode()等可能通过缓存定时泄漏有关您的加密密钥的信息。尽可能避免他们。

Even if you follow the advice given here, a lot can go wrong with cryptography. Always have a cryptography expert review your implementation. If you are not fortunate enough to be personal friends with a cryptography student at your local university, you can always try the Cryptography Stack Exchange forum for advice.

即使你遵循这里给出的建议,密码术也会出错。始终要有一个密码专家检查您的实现。如果你没有足够的幸运成为当地大学的密码学学生的私人朋友,你可以尝试一下密码学栈交流论坛。

If you need a professional analysis of your implementation, you can always hire a reputable team of security consultants to review your PHP cryptography code (disclosure: my employer).

如果您需要对您的实现进行专业的分析,您可以一直雇佣一个有信誉的安全顾问团队来检查您的PHP密码代码(披露:我的雇主)。

Important: When to Not Use Encryption

Don't encrypt passwords. You want to hash them instead, using one of these password-hashing algorithms:

不加密的密码。你想用一个密码散列算法来哈希它们,

Never use a general-purpose hash function (MD5, SHA256) for password storage.

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

Don't encrypt URL Parameters. It's the wrong tool for the job.

不加密URL参数。这是这个工作的错误工具。

PHP String Encryption Example with Libsodium

If you are on PHP < 7.2 or otherwise do not have libsodium installed, you can use sodium_compat to accomplish the same result (albeit slower).

如果您在PHP < 7.2或其他情况下没有安装lib钠,您可以使用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 he 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;
}

Then to test it out:

然后测试一下:

<?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 Made Easier

One of the projects I've been working on is an encryption library called Halite, which aims to make libsodium easier and more intuitive.

我一直在做的一个项目是一个名为Halite的加密库,它的目的是使lib钠更容易、更直观。

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

All of the underlying cryptography is handled by libsodium.

所有底层密码学都是由libna处理的。

Example with defuse/php-encryption

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

Note: Crypto::encrypt() returns hex-encoded output.

加密()返回十六进制编码的输出。

Encryption Key Management

If you're tempted to use a "password", stop right now. You need a random 128-bit encryption key, not a human memorable password.

如果你想使用“密码”,现在就停止。你需要一个随机的128位加密密钥,而不是一个令人难忘的密码。

You can store an encryption key for long-term use like so:

您可以存储加密密钥,以便长期使用:

$storeMe = bin2hex($key);

And, on demand, you can retrieve it like so:

根据需求,你可以这样检索:

$key = hex2bin($storeMe);

I strongly recommend just storing a randomly generated key for long-term use instead of any sort of password as the key (or to derive the key).

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

If you're using Defuse's library:

如果你使用的是拆除的图书馆:

"But I really want to use a password."

That's a bad idea, but okay, here's how to do it safely.

这是个坏主意,但好吧,下面是如何安全地做这件事。

First, generate a random key and store it in a constant.

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

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

Note that you're adding extra work and could just use this constant as the key and save yourself a lot of heartache!

请注意,您正在添加额外的工作,并且可以使用这个常量作为键,并为自己节省大量的心痛!

Then use PBKDF2 (like so) to derive a suitable encryption key from your password rather than encrypting with your password directly.

然后使用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
    );
}

Don't just use a 16-character password. Your encryption key will be comically broken.

不要只使用16字符的密码。你的加密密钥将被粉碎。

#2


43  

What not to do

不该做什么

WARNING:
This answer uses ECB. ECB is not an encryption mode, it's only a building block. Using ECB as demonstrated in this answer does not actually encrypt the string securely. Do not use ECB in your code. See Scott's answer for a good solution.

警告:这个答案使用欧洲央行。欧洲央行不是加密模式,它只是一个构建块。在这个答案中使用欧洲央行实际上并没有对字符串加密。不要在代码中使用欧洲央行。看看斯科特给出的解决方案。

I got it on myself. Actually i found some answer on google and just modified something. The result is completely insecure however.

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

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

#3


18  

I'm late to the party, but searching for the correct way to do it I came across this page it was one of the top Google search returns, so I will like to share my view on the problem, which I consider it to be up to date at the time of writing this post (beginning of 2017). From PHP 7.1.0 the mcrypt_decrypt and mcrypt_encrypt is going to be deprecated, so building future proof code should use openssl_encrypt and openssl_decrypt

我迟到了,但寻找正确的方法我偶然发现这个页面是数一数二的谷歌搜索返回,所以我将分享我对这个问题的看法,我认为这是最新的写这篇文章的时候(2017年开始)。从PHP 7.1.0中,mcrypt_decrypt和mcrypt_encrypt将被弃用,因此构建未来的证明代码应该使用openssl_encrypt和openssl_decrypt。

You can do something like:

你可以这样做:

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

Important: This uses ECB mode, which isn't secure. If you want a simple solution without taking a crash course in cryptography engineering, don't write it yourself, just use a library.

重要:这使用了欧洲央行的模式,这是不安全的。如果您想要一个简单的解决方案,而不需要在加密工程中使用速成课程,请不要自己编写,只需使用一个库即可。

You can use any other chipper methods as well, depending on your security need. To find out the available chipper methods please see the openssl_get_cipher_methods function.

您还可以使用任何其他的chipper方法,这取决于您的安全需求。要找到可用的chipper方法,请查看openssl_get_cipher_methods函数。

#4


8  

For Laravel framework

If you are using Laravel framework then it's more easy to encrypt and decrypt with internal functions.

如果您使用的是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);

Note: Be sure to set a 16, 24, or 32 character random string in the key option of the config/app.php file. Otherwise, encrypted values will not be secure.

注意:确保在配置/应用程序的关键选项中设置16、24或32字符的随机字符串。php文件。否则,加密的值将不安全。

#5


6  

Historical Note: This was written at the time of PHP4. This is what we call "legacy code" now.

历史记录:这是在PHP4的时候写的。这就是我们现在所说的“遗留代码”。

I have left this answer for historical purposes - but some of the methods are now deprecated, DES encryption method is not a recommended practice, etc.

我已经将这个答案留给了历史目的——但是有些方法现在已经过时了,DES加密方法不是推荐的做法,等等。

I have not updated this code for two reasons: 1) I no longer work with encryption methods by hand in PHP, and 2) this code still serves the purpose it was intended for: to demonstrate the minimum, simplistic concept of how encryption can work in PHP.

我没有更新这段代码有两个原因:1)我不再使用PHP的加密方法,2)这段代码仍然符合它的目的:演示加密如何在PHP中工作的最小的、简单的概念。

If you find a similarly simplistic, "PHP encryption for dummies" kind of source that can get people started in 10-20 lines of code or less, let me know in comments.

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

Beyond that, please enjoy this Classic Episode of early-era PHP4 minimalistic encryption answer.

除此之外,请欣赏这一经典的早期PHP4简约加密回答。


Ideally you have - or can get - access to the mcrypt PHP library, as its certainly popular and very useful a variety of tasks. Here's a run down of the different kinds of encryption and some example code: Encryption Techniques in PHP

理想情况下,您可以访问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 ); 
?> 

A few warnings:

一些警告:

1) Never use reversible, or "symmetric" encryption when a one-way hash will do.

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

2) If the data is truly sensitive, like credit card or social security numbers, stop; you need more than any simple chunk of code will provide, but rather you need a crypto library designed for this purpose and a significant amount of time to research the methods necessary. Further, the software crypto is probably <10% of security of sensitive data. It's like rewiring a nuclear power station - accept that the task is dangerous and difficult and beyond your knowledge if that's the case. The financial penalties can be immense, so better to use a service and ship responsibility to them.

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

3) Any sort of easily implementable encryption, as listed here, can reasonably protect mildly important information that you want to keep from prying eyes or limit exposure in the case of accidental/intentional leak. But seeing as how the key is stored in plain text on the web server, if they can get the data they can get the decryption key.

3)任何一种易于实现的加密,如这里所列,可以合理地保护您想要避免窥视的重要信息,或者在意外/故意泄漏的情况下限制暴露。但是,如果将密钥存储在web服务器上的纯文本中,如果它们能够获得数据,就可以获得解密密钥。

Be that as it may, have fun :)

尽管如此,祝你愉快:)