在 PHP 中加密和解密密码、文件的完美方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12032220/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Perfect way to encrypt & decrypt password, files in PHP?
提问by 1myb
I did a series of research on this topic, but unfortunately I couldn't find a perfect way to encrypt and decrypt files in PHP. Which mean what I'm trying to do is find some way to encrypt & decrypt my items without worry of cracker knew my algorithm. If some algorithm that need to secrete & hide, it can't solve my problems while once the logic shared through anywhere, or they broke into my server and get the source file, then it should be some way to decrypt it using the same decryption algorithm. Previously I found several great posts on StackOverFlow website, but it still couldn't answer my question.
我对这个主题做了一系列的研究,但不幸的是我找不到在 PHP 中加密和解密文件的完美方法。这意味着我想要做的是找到某种方法来加密和解密我的项目,而不必担心破解者知道我的算法。如果某些算法需要隐藏和隐藏,一旦逻辑通过任何地方共享,它就无法解决我的问题,或者他们闯入我的服务器并获取源文件,那么它应该是使用相同解密的某种方式来解密它算法。以前我在 StackOverFlow 网站上找到了几篇很棒的帖子,但它仍然无法回答我的问题。
The best way to encrypt password of the world, from what I conclude through reading. Blowfish encryption. It's one way hashing algorithm with 1000's times iteration which make cracker need 7 years to decrypt by using the same specification GPU.
加密世界密码的最佳方法,从我通过阅读得出的结论。河豚加密。这是一种具有1000次迭代的散列算法,这使得破解者使用相同规格的GPU需要7年才能解密。
Obviously, this makes it impossible to decrypt while it's one-way hashing.
显然,这使得在单向散列时无法解密。
- How do you use bcrypt for hashing passwords in PHP?
- Why do salts make dictionary attacks 'impossible'?
The best way to encrypt and decrypt password in PHP, as this question quote as it is. Refer to what I found through the web, sha1 and md5 both are cracked & broken algorithm, even we change the algorithm from
在 PHP 中加密和解密密码的最佳方法,正如这个问题所引用的那样。参考我通过网络发现的,sha1和md5都是破解和损坏的算法,即使我们将算法从
$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $string, MCRYPT_MODE_CBC, md5(md5($key))));
To
到
$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, sha1(md5($key)), $string, MCRYPT_MODE_CBC, sha1(md5(md5($key)))));
Are not it's just increasing the toughness to decrypt it but still crack-able while just time issue ?
这不是只是增加了解密它的韧性,但在时间问题上仍然可以破解吗?
I'm thinking of using our server processor / harddisc GUID to generate the salt and encrypt the password.
我正在考虑使用我们的服务器处理器/硬盘 GUID 来生成盐并加密密码。
It's still some stupid way to do while cracker got the access to the server and they can just use PHP to echo the GUID and do the decryption. Or if it works, a few years later my website will be in trouble. The reason is harddisc, processor never last forever. When the time my processor or harddisc down, it's a time when my website down and lost all the credential.
当破解者获得对服务器的访问权限时,这仍然是一些愚蠢的方法,他们可以只使用 PHP 来回显 GUID 并进行解密。或者,如果它有效,几年后我的网站将陷入困境。原因是硬盘,处理器永远不会长久。当我的处理器或硬盘停机时,就是我的网站停机并丢失所有凭据的时候。
Update
更新
Found this question which doing with blowfish for decryption in PHP. Is it solving the question of finding secured way to encrypt and hard to decrypt by others ?
发现这个问题是用河豚在 PHP 中解密的。它是否解决了寻找安全加密方法和其他人难以解密的问题?
Can anyone please suggest on how should I overcome this issue ? Thanks.
任何人都可以建议我应该如何克服这个问题?谢谢。
采纳答案by rajukoyilandy
Checkout this well documented article A reversible password encryption routine for PHP, intended for those PHP developers who want a password encryption routine that is reversible.
查看这篇有据可查的文章PHP 的可逆密码加密例程,适用于那些想要可逆密码加密例程的PHP 开发人员。
Even though this class is intended for password encryption, you can use it for encryption/decryption of any text.
即使此类旨在用于密码加密,您也可以将其用于任何文本的加密/解密。
function encryption_class() {
$this->errors = array();
// Each of these two strings must contain the same characters, but in a different order.
// Use only printable characters from the ASCII table.
// Do not use single quote, double quote or backslash as these have special meanings in PHP.
// Each character can only appear once in each string.
$this->scramble1 = '! #$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~';
$this->scramble2 = 'f^jAE]okIOzU[2&q1{3`h5w_794p@6s8?BgP>dFV=m D<TcS%Ze|r:lGK/uCy.Jx)HiQ!#$~(;Lt-R}Ma,NvW+Ynb*0X';
if (strlen($this->scramble1) <> strlen($this->scramble2)) {
trigger_error('** SCRAMBLE1 is not same length as SCRAMBLE2 **', E_USER_ERROR);
} // if
$this->adj = 1.75; // this value is added to the rolling fudgefactors
$this->mod = 3; // if divisible by this the adjustment is made negative
}
Caution:
注意:
If you are using PHP version >= 5.3.3, then you have to change the class name from encryption_classto __construct
如果您使用的是 PHP 版本 >= 5.3.3,那么您必须将类名从 更改encryption_class为__construct
Reason:
原因:
As of PHP 5.3.3, methods with the same name as the last element of a namespaced class name will no longer be treated as constructor.
As of PHP 5.3.3, methods with the same name as the last element of a namespaced class name will no longer be treated as constructor.
Usage:
用法:
$crypt = new encryption_class();
$crypt->setAdjustment(1.75); // 1st adjustment value (optional)
$crypt->setModulus(3); // 2nd adjustment value (optional)
/**
*
* @param string $key - Your encryption key
* @param string $sourceText - The source text to be encrypted
* @param integer $encLen - positive integer indicating the minimum length of encrypted text
* @return string - encrypted text
*/
$encrypt_result = $crypt->encrypt($key, $sourceText, $encLen);
/**
*
* @param string $key - Your encryption key (same used for encryption)
* @param string $encrypt_result - The text to be decrypted
* @return string - decrypted text
*/
$decrypt_result = $crypt->decrypt($key, $encrypt_result);
Update:
更新:
Above class is not intended for encrypting files, but you can!!!
以上课程不是用于加密文件,但您可以!!!
base64_encodeyour source text (file contents)- for actual encryption, apply above enc/dec class over base64-encoded text
- for decryption, apply above enc/dec class over actually encrypted text
base64_decodewill give you the actual file contents (you can save a copy of file with this content)
base64_encode您的源文本(文件内容)- 对于实际加密,将上面的 enc/dec 类应用于 base64 编码的文本
- 对于解密,将上面的 enc/dec 类应用于实际加密的文本
base64_decode会给你实际的文件内容(你可以用这个内容保存文件的副本)
I've encrypted an image, decrypted back and saved to a new file!!! checkout the code.
我已经加密了一个图像,解密回来并保存到一个新文件中!!!签出代码。
//class for encrypt/decrypt routines
require 'class.encryption.php';
//configuring your security levels
$key = 'This is my secret key; with symbols (@$^*&<?>/!#_+), cool eh?!!! :)';
$adjustment = 1.75;
$modulus = 2;
//customizing
$sourceFileName = 'source-image.png';
$destFileName = 'dest-image.png';
$minSpecifiedLength = 512;
//base64 encoding file contents, to get all characters in our range
//binary too!!!
$sourceText = base64_encode(file_get_contents($sourceFileName));
$crypt = new encryption_class();
$crypt->setAdjustment($adjustment); //optional
$crypt->setModulus($modulus); //optional
//encrypted text
$encrypt_result = $crypt->encrypt($key, $sourceText, $minSpecifiedLength);
//receive initial file contents after decryption
$decrypt_result = base64_decode($crypt->decrypt($key, $encrypt_result));
//save as new file!!!
file_put_contents($destFileName, $decrypt_result);
回答by Niet the Dark Absol
Bear in mind that, in order to crack passwords, a hacker would have to have access to the encrypted passwords in the first place. In order to do that they would have to compromise the server's security, which should be impossible if the site is coded correctly (proper escaping or prepared statements).
请记住,为了破解密码,黑客首先必须能够访问加密的密码。为了做到这一点,他们将不得不损害服务器的安全性,如果站点编码正确(正确的转义或准备好的语句),这应该是不可能的。
One of the strongest yet simplest forms of encryption is XOR, however it is entirely dependent on the key. If the key is the same length as the encoded text, then it is completely unbreakable without that key. Even having the key half the length of the text is extremely unlikely to be broken.
XOR 是最强大但最简单的加密形式之一,但它完全依赖于密钥。如果密钥与编码文本的长度相同,那么没有该密钥它是完全牢不可破的。即使密钥只有文本长度的一半,也极不可能被破解。
In the end, though, whatever method you choose is secured by your FTP/SSH/whatever password that allows you to access the server's files. If your own password is compromised, a hacker can see everything.
但是,最终,无论您选择哪种方法,都将受到您的 FTP/SSH/允许您访问服务器文件的任何密码的保护。如果您自己的密码被泄露,黑客可以看到一切。
回答by martinstoeckli
Your question leads to two different answers. It's an important difference, whether you need to decrypt the data later (like files), or if you can use a one way hash (for passwords).
你的问题会导致两个不同的答案。这是一个重要的区别,无论您是需要稍后解密数据(如文件),还是可以使用单向哈希(用于密码)。
One-Way-Hash
单向哈希
If you do not need to decryptyour data (passwords), you should use a hash function. This is safer, because even if an attacker has control over your server and your database, he should not be able to retrieve the original password. Since users often use their password for several websites, at least he doesn't gain access to other sites as well.
如果您不需要解密您的数据(密码),您应该使用散列函数。这更安全,因为即使攻击者控制了您的服务器和数据库,他也不应该能够检索原始密码。由于用户经常在几个网站上使用他们的密码,至少他也无法访问其他网站。
As you already stated, one of the most recommended hash functions today, is bcrypt. Despite it's origin in the blowfish algorithm, it is in fact a hash function (not encryption). Bcrypt was designed especially to hash passwords, and is therefore slow (needs computing time). It's recommended to use a well established library like phpass, and if you want to understand how to implement it, you can read this article, where i tried to explain the most important points.
正如您已经说过的,今天最推荐的哈希函数之一是bcrypt。尽管它起源于河豚算法,但它实际上是一个散列函数(不是加密)。Bcrypt 是专门为散列密码而设计的,因此速度很慢(需要计算时间)。建议使用像phpass这样的完善的库,如果你想了解如何实现它,你可以阅读这篇文章,我试图解释最重要的点。
Encryption
加密
If you need to decryptyour data later (files), you cannot prevent, that an attacker with control over your server, can decrypt the files as well (after all the server has to be able to decrypt it). All adds up to the question of where to store the secret key. The only thing you can do, is to make it harder to get the key.
如果您稍后需要解密您的数据(文件),则无法防止控制您的服务器的攻击者也可以解密文件(毕竟服务器必须能够对其进行解密)。所有这些加起来就是将密钥存储在哪里的问题。你唯一能做的就是让你更难拿到钥匙。
That means, if you store the key in a file, it should be outsidethe http root directory, so it can on no account be accessed from the internet. You could store it on a different server, so the attacker would need control over both servers, though then you face the problem of the secure communication between the servers. In every case, you can make theft harder, but you cannot prevent it completely.
这意味着,如果您将密钥存储在一个文件中,它应该位于http 根目录之外,因此任何帐户都无法从 Internet 访问它。您可以将其存储在不同的服务器上,因此攻击者需要控制两台服务器,但随后您将面临服务器之间安全通信的问题。在任何情况下,您都可以使盗窃变得更加困难,但您无法完全防止盗窃。
Depending on your scenario, you could encrypt the files locally on your computer, and only store the encrypted files on the server. The server would not be able to decrypt the files on it's own then, so they are safe.
根据您的情况,您可以在您的计算机本地加密文件,并将加密的文件仅存储在服务器上。服务器将无法自行解密文件,因此它们是安全的。
回答by Adam11
So you already know about salting and hashing, but you can also "stretch" your passwords, where instead of just hashing each password once, you hash it several thousand times. This will slow down brute force attacks and increase the lifespan of your hashing algorithm. Interestingly it works by intentionally slowing down your server...
What I would recommend is writing your own custom hash function. First, you add salt to the password, then you pick a hash algorithm (say sha512, or perhaps a newer algorithm that is designed to be inefficient for this very purpose) and hash it, say, 10,000 times, then store it in the database. And as you already know, when a user logs in, instead of reversing the hash, you simply run their input through the same algorithm and see if it matches.
The beauty of writing your own hash function is that when it comes time to update your hash algorithm because the old one has become vulnerable to brute force attacks, all you have to do is add to your hash function, taking the result of the old hash algorithm, re-salting it, and hashing it again using your new algorithm. You can use whatever hash algorithm is considered secure at the time. Then, you can simply re-hash every password already stored in your database with the new part of your hash function, thus ensuring backwards compatibility. Depending on how many users you have and how fast your server is, it might only take a couple of seconds to perform this update.
There is still a vulnerability, however. If a hacker has an old copy of your database and cracks it, he still knows the passwords of any users who haven't changed their passwords yet. The only way around this is to require your users to occasionally change their passwords, which may or may not be suitable for your site depending on the nature of the information it contains. Some security professionals suggest that users only change their passwords if they are compromised because if the system makes it too difficult to manage passwords, they will begin doing insecure things like keeping their passwords under their keyboards, which for some organizations is a bigger threat than having users that never change their passwords. If your website is a forum or review site or something of that nature, you should consider how much users have to lose by having their account hacked, how easy it is to restore their data to the way it was before it was hacked, and whether they will consider your site worth updating their password for if your password policy is too annoying.
因此,您已经了解了加盐和散列,但您也可以“扩展”您的密码,而不是只对每个密码散列一次,而是将其散列数千次。这将减慢蛮力攻击并延长散列算法的寿命。有趣的是,它的工作原理是有意减慢您的服务器速度……
我建议您编写自己的自定义哈希函数。首先,您在密码中添加盐,然后选择一个散列算法(比如 sha512,或者可能是一种设计用于此目的而效率低下的较新算法)并对其进行散列,比如 10,000 次,然后将其存储在数据库中. 正如您已经知道的那样,当用户登录时,您只需通过相同的算法运行他们的输入并查看是否匹配,而不是反转散列。
编写自己的散列函数的美妙之处在于,当需要更新散列算法时,因为旧的算法容易受到蛮力攻击,您所要做的就是添加到您的散列函数中,获取旧散列的结果算法,对其重新加盐,然后使用您的新算法再次对其进行散列。您可以使用当时被认为安全的任何哈希算法。然后,您可以简单地使用散列函数的新部分重新散列已存储在数据库中的每个密码,从而确保向后兼容性。根据您拥有的用户数量和服务器的速度,执行此更新可能只需要几秒钟。
然而,仍然存在漏洞。如果黑客拥有您数据库的旧副本并对其进行破解,他仍然知道尚未更改密码的任何用户的密码。解决此问题的唯一方法是要求您的用户偶尔更改他们的密码,这可能适合或不适合您的网站,具体取决于它包含的信息的性质。一些安全专家建议,用户只有在受到威胁时才更改密码,因为如果系统使管理密码变得太困难,他们就会开始做不安全的事情,例如将密码保存在键盘下,这对某些组织来说是比拥有密码更大的威胁从不更改密码的用户。如果您的网站是论坛或评论网站或类似性质的网站,
One possible hash function:
一种可能的哈希函数:
function the_awesomest_hash($password)
{
$salt1 = "awesomesalt!";
$password = $salt1 . $password;
for($i = 0; $i < 10000; $i++)
{
$password = hash('sha512', $password);
}
// Some time has passed, and you have added to your hash function
$salt2 = "niftysalt!";
$password = $salt2 . $password;
for($i = 0; $i < 10000; $i++)
{
$password = hash('futuresuperhash1024', $password);
}
return $password;
}
Now, in order to update all the passwords already in your database, you would run them through this function:
现在,为了更新数据库中已有的所有密码,您可以通过以下函数运行它们:
function update_hash($password)
{
// This is the last part of your the_awesomest_hash() function
$salt2 = "niftysalt!";
$password = $salt2 . $password;
for($i = 0; $i < 10000; $i++)
{
$password = hash('futuresuperhash1024', $password);
}
return $password;
}
I like to write my own hash functions because it's easier to keep track of what exactly is happening for when it comes time to update them.
我喜欢编写自己的哈希函数,因为在更新它们时更容易跟踪到底发生了什么。
回答by Maarten Bodewes
After some study of PHP, particularly the random number generation, the only way to securely encrypt with PHP is by using an OpenSSL wrapper. Especially the creators of mcrypt are a bunch of morons, just look at the example of not how to perform cryptography in their sample:
经过对 PHP 的一些研究,特别是随机数生成,使用 PHP 进行安全加密的唯一方法是使用 OpenSSL 包装器。尤其是 mcrypt 的创造者是一群白痴,看看他们的样本中没有如何执行密码学的例子:
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "This is a very secret key";
$text = "Meet me at 11 o'clock behind the monument.";
echo strlen($text) . "\n";
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);
echo strlen($crypttext) . "\n";
Note that by default MCRYPT_RAND is not seeded well. Furthermore, there is at least about 5 mistakes in above code alone, and they won't fix it.
请注意,默认情况下 MCRYPT_RAND 没有很好地播种。此外,仅在上面的代码中至少有大约 5 个错误,他们不会修复它。
[EDIT] See below for an ammended sample. Note that this sample is not very safe either (as explained above). Furthermore normally you should not encrypt passwords...
[编辑] 有关修订后的示例,请参见下文。请注意,此示例也不是很安全(如上所述)。此外通常你不应该加密密码......
# the key should be random binary, use scrypt, bcrypt or PBKDF2 to convert a string into a key
# key is specified using hexadecimals
$key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");
echo "Key size (in bits): " . $key_size * 8 . "\n";
$plaintext = "This string was AES-256 / CBC / ZeroBytePadding encrypted.";
echo "Plain text: " . $plain_text . "\n";
$ciphertext_base64 = encryptText($key, $plaintext);
echo $ciphertext_base64 . "\n";
function encryptText(string $key_hex, string $plaintext) {
# --- ENCRYPTION ---
# show key size use either 16, 24 or 32 byte keys for AES-128, 192 and 256 respectively
$key_size = strlen($key);
# create a random IV to use with CBC encoding
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
# use an explicit encoding for the plain text
$plaintext_utf8 = utf8_encode($plaintext);
# creates a cipher text compatible with AES (Rijndael block size = 128) to keep the text confidential
# only suitable for encoded input that never ends with value 00h (because of default zero padding)
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext_utf8, MCRYPT_MODE_CBC, $iv);
# prepend the IV for it to be available for decryption
$ciphertext = $iv . $ciphertext;
# encode the resulting cipher text so it can be represented by a string
$ciphertext_base64 = base64_encode($ciphertext);
return $ciphertext_base64;
}
# === WARNING ===
# Resulting cipher text has no integrity or authenticity added
# and is not protected against padding oracle attacks.
# --- DECRYPTION ---
$ciphertext_dec = base64_decode($ciphertext_base64);
# retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
$iv_dec = substr($ciphertext_dec, 0, $iv_size);
# retrieves the cipher text (everything except the $iv_size in the front)
$ciphertext_dec = substr($ciphertext_dec, $iv_size);
# may remove 00h valued characters from end of plain text
$plaintext_utf8_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
echo $plaintext_utf8_dec . "\n";
回答by Jahanzab Anwar
So far i know the best way to save password is with salted hash like used in joomla. You can also add extra keys to md5 hash along with traditional base64.I wrote a script like that sometime ago, tried to find it but can't.
到目前为止,我知道保存密码的最佳方法是使用 joomla 中使用的加盐哈希。您还可以将额外的键与传统的 base64 一起添加到 md5 哈希中。我前段时间写了一个类似的脚本,试图找到它但找不到。
Joomla uses salted md5 passwords. Take the hashed password you gave: 30590cccd0c7fd813ffc724591aea603:WDmIt53GwY2X7TvMqDXaMWJ1mrdZ1sKb
Joomla 使用盐渍的 md5 密码。获取您提供的散列密码:30590cccd0c7fd813ffc724591aea603:WDmIt53GwY2X7TvMqDXaMWJ1mrdZ1sKb
If your password was say 'password', then: md5('passwordWDmIt53GwY2X7TvMqDXaMWJ1mrdZ1sKb') = 30590cccd0c7fd813ffc724591aea603
如果您的密码是“密码”,则: md5('passwordWDmIt53GwY2X7TvMqDXaMWJ1mrdZ1sKb') = 30590cccd0c7fd813ffc724591aea603
So, take your password. Generate a random 32 character string. Compute the md5 of the password concatenated with the random string. Store the md5 result plus a : plus the random 32 character string in the database.
所以,拿你的密码。生成一个随机的 32 个字符的字符串。计算与随机字符串连接的密码的 md5。将 md5 结果加上 : 加上随机的 32 个字符串存储在数据库中。

