What I mean is:
我的意思是:
Original String + Salt or Key --> Encrypted StringEncrypted 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,但它对我不起作用。
6 个解决方案
#1
337
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现在提供libsodium!已更新至PHP 7.2或更高版本,仅遵循此答案中的libsodium建议。
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访问权限,请使用libsodium(如果您想要没有PECL的libsodium,则使用sodium_compat);否则......使用defuse / 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.
如果您仍然希望编写和部署自己的加密库,而不是互联网上每个加密专家的传统智慧,那么这些步骤就是您必须采取的步骤。
Encryption:
- 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.
- 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!
在CTR模式下使用AES加密。您也可以使用GCM(不需要单独的MAC)。此外,ChaCha20和Salsa20(由libsodium提供)是流密码,不需要特殊模式。
除非您选择上面的GCM,否则您应该使用HMAC-SHA-256对密文进行身份验证(或者,对于流密码,Poly1305 - 大多数libsodium API都会为您执行此操作)。 MAC应该覆盖IV和密文!
Decryption:
- 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. - Decrypt the message.
除非使用Poly1305或GCM,否则重新计算密文的MAC并将其与使用hash_equals()发送的MAC进行比较。如果失败,则中止。
解密消息。
Other Design Considerations:
- 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).
- Make sure you use
mb_strlen()
andmb_substr()
, using the'8bit'
character set mode to preventmbstring.func_overload
issues. - IVs should be generating using a CSPRNG; If you're using
mcrypt_create_iv()
, DO NOT USEMCRYPT_RAND
!- Also check out random_compat.
还可以查看random_compat。
- Unless you're using an AEAD construct, ALWAYS encrypt then MAC!
-
bin2hex()
,base64_encode()
, etc. may leak information about your encryption keys via cache timing. Avoid them if possible.
不要压缩任何东西。密文不可压缩;在加密之前压缩明文可能导致信息泄露(例如,TLS上的CRIME和BREACH)。
确保使用mb_strlen()和mb_substr(),使用'8bit'字符集模式来防止mbstring.func_overload问题。
IV应该使用CSPRNG生成;如果你正在使用mcrypt_create_iv(),请不要使用MCRYPT_RAND!还可以查看random_compat。
除非你使用AEAD结构,否则总是加密MAC!
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.
即使你遵循这里给出的建议,密码学也会出现很多问题。始终有一位加密专家审核您的实施。如果您没有幸运能够与当地大学的密码学生成为私人朋友,您可以随时尝试使用Cryptography Stack Exchange论坛获取建议。
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:
不要加密密码。您希望使用以下密码散列算法之一来散列它们:
- Argon2
- scrypt
- bcrypt
- PBKDF2-SHA256 with 86,000 iterations
PBKDF2-SHA256具有86,000次迭代
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或者没有安装libsodium,你可以使用sodium_compat来实现相同的结果(尽管速度较慢)。
<?phpdeclare(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;}
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的加密库,旨在使libsodium更容易,更直观。
<?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: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.
所有底层加密都由libsodium处理。
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.
注意:Crypto :: encrypt()返回十六进制编码的输出。
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:
如果您正在使用Defuse的库:
$string = $keyObject->saveToAsciiSafeString()
$loaded = Key::loadFromAsciiSafeString($string);
$ string = $ keyObject-> saveToAsciiSafeString()
$ loaded = Key :: loadFromAsciiSafeString($ string);
"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
42
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.警告:此答案使用ECB。 ECB不是加密模式,它只是一个构建块。如本答案中所示,使用ECB实际上并不安全地加密字符串。不要在代码中使用ECB。请参阅Scott的答案以获得一个好的解决方案。
I got it on myself. Actually i found some answer on google and just modified something. The result is completely insecure however.
我得到了自己。实际上我在谷歌找到了一些答案,只是修改了一些东西。结果却完全不安全。
<?phpdefine("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
28
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.
重要提示:这使用ECB模式,这是不安全的。如果您想要一个简单的解决方案,而不需要在加密工程中学习速成课程,请不要自己编写,只需使用库。
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.
您也可以使用任何其他削片机方法,具体取决于您的安全需求。要找到可用的削片机方法,请参阅openssl_get_cipher_methods函数。
#4
11
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.
注意:确保在config / app.php文件的key选项中设置16个,24个或32个字符的随机字符串。否则,加密值将不安全。
#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)如果数据真正敏感,如信用卡或社会安全号码,请停止;你需要的不仅仅是任何简单的代码块,而是你需要一个为此目的而设计的加密库,并且需要大量的时间来研究必要的方法。此外,软件加密可能是敏感数据安全性的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 :)
尽管如此,玩得开心:)
#6
2
If you don't want to use library (which you should) then use something like this (PHP 7):
如果你不想使用库(你应该),那么使用这样的东西(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);
#1
337
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现在提供libsodium!已更新至PHP 7.2或更高版本,仅遵循此答案中的libsodium建议。
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访问权限,请使用libsodium(如果您想要没有PECL的libsodium,则使用sodium_compat);否则......使用defuse / 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.
如果您仍然希望编写和部署自己的加密库,而不是互联网上每个加密专家的传统智慧,那么这些步骤就是您必须采取的步骤。
Encryption:
- 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.
- 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!
在CTR模式下使用AES加密。您也可以使用GCM(不需要单独的MAC)。此外,ChaCha20和Salsa20(由libsodium提供)是流密码,不需要特殊模式。
除非您选择上面的GCM,否则您应该使用HMAC-SHA-256对密文进行身份验证(或者,对于流密码,Poly1305 - 大多数libsodium API都会为您执行此操作)。 MAC应该覆盖IV和密文!
Decryption:
- 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. - Decrypt the message.
除非使用Poly1305或GCM,否则重新计算密文的MAC并将其与使用hash_equals()发送的MAC进行比较。如果失败,则中止。
解密消息。
Other Design Considerations:
- 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).
- Make sure you use
mb_strlen()
andmb_substr()
, using the'8bit'
character set mode to preventmbstring.func_overload
issues. - IVs should be generating using a CSPRNG; If you're using
mcrypt_create_iv()
, DO NOT USEMCRYPT_RAND
!- Also check out random_compat.
还可以查看random_compat。
- Unless you're using an AEAD construct, ALWAYS encrypt then MAC!
-
bin2hex()
,base64_encode()
, etc. may leak information about your encryption keys via cache timing. Avoid them if possible.
不要压缩任何东西。密文不可压缩;在加密之前压缩明文可能导致信息泄露(例如,TLS上的CRIME和BREACH)。
确保使用mb_strlen()和mb_substr(),使用'8bit'字符集模式来防止mbstring.func_overload问题。
IV应该使用CSPRNG生成;如果你正在使用mcrypt_create_iv(),请不要使用MCRYPT_RAND!还可以查看random_compat。
除非你使用AEAD结构,否则总是加密MAC!
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.
即使你遵循这里给出的建议,密码学也会出现很多问题。始终有一位加密专家审核您的实施。如果您没有幸运能够与当地大学的密码学生成为私人朋友,您可以随时尝试使用Cryptography Stack Exchange论坛获取建议。
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:
不要加密密码。您希望使用以下密码散列算法之一来散列它们:
- Argon2
- scrypt
- bcrypt
- PBKDF2-SHA256 with 86,000 iterations
PBKDF2-SHA256具有86,000次迭代
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或者没有安装libsodium,你可以使用sodium_compat来实现相同的结果(尽管速度较慢)。
<?phpdeclare(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;}
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的加密库,旨在使libsodium更容易,更直观。
<?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: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.
所有底层加密都由libsodium处理。
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.
注意:Crypto :: encrypt()返回十六进制编码的输出。
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:
如果您正在使用Defuse的库:
$string = $keyObject->saveToAsciiSafeString()
$loaded = Key::loadFromAsciiSafeString($string);
$ string = $ keyObject-> saveToAsciiSafeString()
$ loaded = Key :: loadFromAsciiSafeString($ string);
"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
42
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.警告:此答案使用ECB。 ECB不是加密模式,它只是一个构建块。如本答案中所示,使用ECB实际上并不安全地加密字符串。不要在代码中使用ECB。请参阅Scott的答案以获得一个好的解决方案。
I got it on myself. Actually i found some answer on google and just modified something. The result is completely insecure however.
我得到了自己。实际上我在谷歌找到了一些答案,只是修改了一些东西。结果却完全不安全。
<?phpdefine("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
28
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.
重要提示:这使用ECB模式,这是不安全的。如果您想要一个简单的解决方案,而不需要在加密工程中学习速成课程,请不要自己编写,只需使用库。
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.
您也可以使用任何其他削片机方法,具体取决于您的安全需求。要找到可用的削片机方法,请参阅openssl_get_cipher_methods函数。
#4
11
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.
注意:确保在config / app.php文件的key选项中设置16个,24个或32个字符的随机字符串。否则,加密值将不安全。
#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)如果数据真正敏感,如信用卡或社会安全号码,请停止;你需要的不仅仅是任何简单的代码块,而是你需要一个为此目的而设计的加密库,并且需要大量的时间来研究必要的方法。此外,软件加密可能是敏感数据安全性的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 :)
尽管如此,玩得开心:)
#6
2
If you don't want to use library (which you should) then use something like this (PHP 7):
如果你不想使用库(你应该),那么使用这样的东西(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);