php 双向加密:我需要存储可以找回的密码

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5089841/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 16:46:18  来源:igfitidea点击:

Two-way encryption: I need to store passwords that can be retrieved

phpsecurityencryptionpasswords

提问by HyderA

I am creating an application that will store passwords, which the user can retrieve and see. The passwords are for a hardware device, so checking against hashes are out of the question.

我正在创建一个将存储密码的应用程序,用户可以检索和查看密码。密码用于硬件设备,因此检查哈希值是不可能的。

What I need to know is:

我需要知道的是:

  1. How do I encrypt and decrypt a password in PHP?

  2. What is the safest algorithm to encrypt the passwords with?

  3. Where do I store the private key?

  4. Instead of storing the private key, is it a good idea to require users to enter the private key any time they need a password decrypted? (Users of this application can be trusted)

  5. In what ways can the password be stolen and decrypted? What do I need to be aware of?

  1. 如何在 PHP 中加密和解密密码?

  2. 加密密码的最安全算法是什么?

  3. 我在哪里存储私钥?

  4. 不是存储私钥,而是要求用户在需要解密密码时输入私钥是一个好主意吗?(此应用程序的用户可以信任)

  5. 密码可以通过哪些方式被窃取和解密?我需要注意什么?

回答by ircmaxell

Personally, I would use mcryptlike others posted. But there is much more to note...

就个人而言,我会mcrypt像其他人一样使用。但是还有很多需要注意的......

  1. How do I encrypt and decrypt a password in PHP?

    See below for a strong class that takes care of everything for you:

  2. What is the safest algorithm to encrypt the passwords with?

    safest? any of them. The safest method if you're going to encrypt is to protect against information disclosure vulnerabilities (XSS, remote inclusion, etc). If it gets out, the attacker can eventually crack the encryption (no encryption is 100% un-reversible without the key - As @NullUserException points out this is not entirely true. There are some encryption schemes that are impossible to crack such as OneTimePad).

  3. Where do I store the private key?

    What I would do is use 3 keys. One is user supplied, one is application specific and the other is user specific (like a salt). The application specific key can be stored anywhere (in a config file outside of the web-root, in an environmental variable, etc). The user specific one would be stored in a column in the db next to the encrypted password. The user supplied one would not be stored. Then, you'd do something like this:

    $key = $userKey . $serverKey . $userSuppliedKey;
    

    The benefit there, is that any 2 of the keys can be compromised without the data being compromised. If there's a SQL Injection attack, they can get the $userKey, but not the other 2. If there's a local server exploit, they can get $userKeyand $serverKey, but not the third $userSuppliedKey. If they go beat the user with a wrench, they can get the $userSuppliedKey, but not the other 2 (but then again, if the user is beaten with a wrench, you're too late anyway).

  4. Instead of storing the private key, is it a good idea to require users to enter the private key any time they need a password decrypted? (Users of this application can be trusted)

    Absolutely. In fact, that's the only way I would do it. Otherwise you'd need to store an unencrypted version in a durable storage format (shared memory such as APC or memcached, or in a session file). That's exposing yourself to additional compromises. Never store the unencrypted version of the password in anything except a local variable.

  5. In what ways can the password be stolen and decrypted? What do I need to be aware of?

    Any form of compromise of your systems will let them view encrypted data. If they can inject code or get to your filesystem, they can view decrypted data (since they can edit the files that decrypt the data). Any form of Replay or MITM attack will also give them full access to the keys involved. Sniffing the raw HTTP traffic will also give them the keys.

    Use SSL for all traffic. And make sure nothing on the server has any kind of vulnerabilities (CSRF, XSS, SQL Injection, Privilege Escalation, Remote Code Execution, etc).

  1. 如何在 PHP 中加密和解密密码?

    请参阅下面的强大课程,为您处理一切:

  2. 加密密码的最安全算法是什么?

    最安全?任何一位。如果您要加密,最安全的方法是防止信息泄露漏洞(XSS、远程包含等)。如果它出去,攻击者最终可以破解加密(没有密钥,没有加密是 100% 不可逆的 - 正如@NullUserException 指出的那样,这并不完全正确。有一些加密方案是不可能破解的,例如OneTimePad) .

  3. 我在哪里存储私钥?

    我会做的是使用 3 个键。一个是用户提供的,一个是特定于应用程序的,另一个是特定于用户的(如盐)。应用程序特定的密钥可以存储在任何地方(在 web 根目录之外的配置文件中,在环境变量中,等等)。特定于用户的密码将存储在数据库中加密密码旁边的一列中。用户提供的一个不会被存储。然后,你会做这样的事情:

    $key = $userKey . $serverKey . $userSuppliedKey;
    

    这样做的好处是,可以在不破坏数据的情况下破坏任何 2 个密钥。如果存在 SQL 注入攻击,他们可以获得$userKey,但不能获得其他 2 个。如果存在本地服务器漏洞,他们可以获得$userKey$serverKey,但不能获得第三个$userSuppliedKey。如果他们用扳手殴打用户,他们可以获得$userSuppliedKey,但不能获得其他 2 个(但话说回来,如果用户被扳手殴打,无论如何你都太晚了)。

  4. 不是存储私钥,而是要求用户在需要解密密码时输入私钥是一个好主意吗?(此应用程序的用户可以信任)

    绝对地。事实上,这是我唯一的办法。否则,您需要以持久存储格式(共享内存,如 APC 或 memcached,或会话文件)存储未加密版本。这会让自己面临额外的妥协。切勿将密码的未加密版本存储在局部变量以外的任何地方。

  5. 密码可以通过哪些方式被窃取和解密?我需要注意什么?

    任何形式的系统入侵都会让他们查看加密数据。如果他们可以注入代码或访问您的文件系统,他们就可以查看解密的数据(因为他们可以编辑解密数据的文件)。任何形式的重放或中间人攻击也将使他们能够完全访问所涉及的密钥。嗅探原始 HTTP 流量也会为他们提供密钥。

    对所有流量使用 SSL。并确保服务器上没有任何类型的漏洞(CSRF、XSS、SQL 注入、权限提升、远程代码执行等)。

Edit:Here's a PHP class implementation of a strong encryption method:

编辑:这是一个强加密方法的 PHP 类实现:

/**
 * A class to handle secure encryption and decryption of arbitrary data
 *
 * Note that this is not just straight encryption.  It also has a few other
 *  features in it to make the encrypted data far more secure.  Note that any
 *  other implementations used to decrypt data will have to do the same exact
 *  operations.  
 *
 * Security Benefits:
 *
 * - Uses Key stretching
 * - Hides the Initialization Vector
 * - Does HMAC verification of source data
 *
 */
class Encryption {

    /**
     * @var string $cipher The mcrypt cipher to use for this instance
     */
    protected $cipher = '';

    /**
     * @var int $mode The mcrypt cipher mode to use
     */
    protected $mode = '';

    /**
     * @var int $rounds The number of rounds to feed into PBKDF2 for key generation
     */
    protected $rounds = 100;

    /**
     * Constructor!
     *
     * @param string $cipher The MCRYPT_* cypher to use for this instance
     * @param int    $mode   The MCRYPT_MODE_* mode to use for this instance
     * @param int    $rounds The number of PBKDF2 rounds to do on the key
     */
    public function __construct($cipher, $mode, $rounds = 100) {
        $this->cipher = $cipher;
        $this->mode = $mode;
        $this->rounds = (int) $rounds;
    }

    /**
     * Decrypt the data with the provided key
     *
     * @param string $data The encrypted datat to decrypt
     * @param string $key  The key to use for decryption
     * 
     * @returns string|false The returned string if decryption is successful
     *                           false if it is not
     */
    public function decrypt($data, $key) {
        $salt = substr($data, 0, 128);
        $enc = substr($data, 128, -64);
        $mac = substr($data, -64);

        list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);

        if (!hash_equals(hash_hmac('sha512', $enc, $macKey, true), $mac)) {
             return false;
        }

        $dec = mcrypt_decrypt($this->cipher, $cipherKey, $enc, $this->mode, $iv);

        $data = $this->unpad($dec);

        return $data;
    }

    /**
     * Encrypt the supplied data using the supplied key
     * 
     * @param string $data The data to encrypt
     * @param string $key  The key to encrypt with
     *
     * @returns string The encrypted data
     */
    public function encrypt($data, $key) {
        $salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
        list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);

        $data = $this->pad($data);

        $enc = mcrypt_encrypt($this->cipher, $cipherKey, $data, $this->mode, $iv);

        $mac = hash_hmac('sha512', $enc, $macKey, true);
        return $salt . $enc . $mac;
    }

    /**
     * Generates a set of keys given a random salt and a master key
     *
     * @param string $salt A random string to change the keys each encryption
     * @param string $key  The supplied key to encrypt with
     *
     * @returns array An array of keys (a cipher key, a mac key, and a IV)
     */
    protected function getKeys($salt, $key) {
        $ivSize = mcrypt_get_iv_size($this->cipher, $this->mode);
        $keySize = mcrypt_get_key_size($this->cipher, $this->mode);
        $length = 2 * $keySize + $ivSize;

        $key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length);

        $cipherKey = substr($key, 0, $keySize);
        $macKey = substr($key, $keySize, $keySize);
        $iv = substr($key, 2 * $keySize);
        return array($cipherKey, $macKey, $iv);
    }

    /**
     * Stretch the key using the PBKDF2 algorithm
     *
     * @see http://en.wikipedia.org/wiki/PBKDF2
     *
     * @param string $algo   The algorithm to use
     * @param string $key    The key to stretch
     * @param string $salt   A random salt
     * @param int    $rounds The number of rounds to derive
     * @param int    $length The length of the output key
     *
     * @returns string The derived key.
     */
    protected function pbkdf2($algo, $key, $salt, $rounds, $length) {
        $size   = strlen(hash($algo, '', true));
        $len    = ceil($length / $size);
        $result = '';
        for ($i = 1; $i <= $len; $i++) {
            $tmp = hash_hmac($algo, $salt . pack('N', $i), $key, true);
            $res = $tmp;
            for ($j = 1; $j < $rounds; $j++) {
                 $tmp  = hash_hmac($algo, $tmp, $key, true);
                 $res ^= $tmp;
            }
            $result .= $res;
        }
        return substr($result, 0, $length);
    }

    protected function pad($data) {
        $length = mcrypt_get_block_size($this->cipher, $this->mode);
        $padAmount = $length - strlen($data) % $length;
        if ($padAmount == 0) {
            $padAmount = $length;
        }
        return $data . str_repeat(chr($padAmount), $padAmount);
    }

    protected function unpad($data) {
        $length = mcrypt_get_block_size($this->cipher, $this->mode);
        $last = ord($data[strlen($data) - 1]);
        if ($last > $length) return false;
        if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) {
            return false;
        }
        return substr($data, 0, -1 * $last);
    }
}

Note that I'm using a function added in PHP 5.6: hash_equals. If you're on lower than 5.6, you can use this substitute function which implements a timing-safe comparisonfunction using double HMAC verification:

请注意,我用PHP 5.6增加了一个功能:hash_equals。如果您的版本低于 5.6,则可以使用此替代函数,该函数使用双重 HMAC 验证实现时序安全比较函数:

function hash_equals($a, $b) {
    $key = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
    return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key);
}

Usage:

用法:

$e = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$encryptedData = $e->encrypt($data, $key);

Then, to decrypt:

然后,解密:

$e2 = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$data = $e2->decrypt($encryptedData, $key);

Note that I used $e2the second time to show you different instances will still properly decrypt the data.

请注意,我$e2第二次向您展示了不同的实例仍会正确解密数据。

Now, how does it work/why use it over another solution:

现在,它是如何工作的/为什么在另一个解决方案上使用它:

  1. Keys

    • The keys are not directly used. Instead, the key is stretched by a standard PBKDF2 derivation.

    • The key used for encryption is unique for every encrypted block of text. The supplied key therefore becomes a "master key". This class therefore provides key rotation for cipher and auth keys.

    • IMPORTANT NOTE, the $roundsparameter is configured for true random keys of sufficient strength (128 bits of Cryptographically Secure random at a minimum). If you are going to use a password, or non-random key (or less random then 128 bits of CS random), you mustincrease this parameter. I would suggest a minimum of 10000 for passwords (the more you can afford, the better, but it will add to the runtime)...

  2. Data Integrity

    • The updated version uses ENCRYPT-THEN-MAC, which is a far better method for ensuring the authenticity of the encrypted data.
  3. Encryption:

    • It uses mcrypt to actually perform the encryption. I would suggest using either MCRYPT_BLOWFISHor MCRYPT_RIJNDAEL_128cyphers and MCRYPT_MODE_CBCfor the mode. It's strong enough, and still fairly fast (an encryption and decryption cycle takes about 1/2 second on my machine).
  1. 钥匙

    • 不直接使用密钥。相反,密钥由标准 PBKDF2 派生扩展。

    • 用于加密的密钥对于每个加密的文本块都是唯一的。因此,提供的密钥成为“主密钥”。因此,此类为密码和身份验证密钥提供密钥轮换。

    • 重要说明,该$rounds参数配置为具有足够强度的真随机密钥(至少 128 位加密安全随机)。如果您要使用密码或非随机密钥(或随机性低于 128 位 CS 随机数),则必须增加此参数。我建议密码至少为 10000(您负担得起的越多越好,但它会增加运行时)...

  2. 数据的完整性

    • 更新版本使用ENCRYPT-THEN-MAC,这是一种更好的确保加密数据真实性的方法。
  3. 加密:

    • 它使用 mcrypt 来实际执行加密。我建议使用MCRYPT_BLOWFISHMCRYPT_RIJNDAEL_128cyphers 和MCRYPT_MODE_CBC模式。它足够强大,而且仍然相当快(在我的机器上加密和解密周期大约需要 1/2 秒)。

Now, as to point 3 from the first list, what that would give you is a function like this:

现在,至于第一个列表中的第 3 点,它会给你一个这样的函数:

function makeKey($userKey, $serverKey, $userSuppliedKey) {
    $key = hash_hmac('sha512', $userKey, $serverKey);
    $key = hash_hmac('sha512', $key, $userSuppliedKey);
    return $key;
}

You could stretch it in the makeKey()function, but since it's going to be stretched later, there's not really a huge point to doing so.

您可以在makeKey()函数中拉伸它,但由于它稍后会被拉伸,因此这样做并没有太大的意义。

As far as the storage size, it depends on the plain text. Blowfish uses a 8 byte block size, so you'll have:

至于存储大小,它取决于纯文本。Blowfish 使用 8 字节的块大小,因此您将拥有:

  • 16 bytes for the salt
  • 64 bytes for the hmac
  • data length
  • Padding so that data length % 8 == 0
  • 盐的 16 个字节
  • hmac 64 字节
  • 数据长度
  • 填充使数据长度 % 8 == 0

So for a 16 character data source, there will be 16 characters of data to be encrypted. So that means the actual encrypted data size is 16 bytes due to padding. Then add the 16 bytes for the salt and 64 bytes for the hmac and the total stored size is 96 bytes. So there's at best a 80 character overhead, and at worst a 87 character overhead...

因此,对于 16 个字符的数据源,将有 16 个字符的数据需要加密。因此,这意味着由于填充,实际加密数据大小为 16 字节。然后为 salt 添加 16 个字节,为 hmac 添加 64 个字节,总存储大小为 96 个字节。所以最多有 80 个字符的开销,最坏的有 87 个字符的开销......

I hope that helps...

我希望这有帮助...

Note:12/11/12: I just updated this class with a MUCH better encryption method, using better derived keys, and fixing the MAC generation...

注意:2012 年 12 月 11 日:我刚刚用更好的加密方法更新了这个类,使用更好的派生密钥,并修复了 MAC 生成......

回答by Ivan

How do I encrypt and decrypt a password in PHP?By implementing one of many encryption algorithms. (or using one of many libraries)

如何在 PHP 中加密和解密密码?通过实施许多加密算法之一。(或使用许多库之一)

What is the safest algorithm to encrypt the passwords with?There are tons of different algorithms, none of which are 100% secure. But many of them are secure enough for commerce and even military purposes

加密密码的最安全算法是什么?有大量不同的算法,但没有一个是 100% 安全的。但其中许多对于商业甚至军事目的来说都足够安全

Where do I store the private key?If you have decided to implement public key - cryptography algorithm(eg RSA), you don't store private key. user have private key. your system has public key which could be stored anywhere you wish.

我在哪里存储私钥?如果您已决定实施公钥 - 密码算法(例如 RSA),则不存储私钥。用户有私钥。您的系统具有可以存储在您希望的任何位置的公钥。

Instead of storing the private key, is it a good idea to require users to enter the private key any time they need a password decrypted? (Users of this application can be trusted)Well if your user can remember ridiculously long prime numbers then - yes, why not. But generally you would need to come up with the system which will allow user to store their key somewhere.

不是存储私钥,而是要求用户在需要解密密码时输入私钥是一个好主意吗?(此应用程序的用户可以信任)好吧,如果您的用户可以记住长得可笑的素数,那么 - 是的,为什么不呢。但通常你需要想出一个系统,允许用户将他们的密钥存储在某个地方。

In what ways can the password be stolen and decrypted? What do I need to be aware of?This depends on the algorithm used. However always make sure that you don't send password unencrypted to or from the user. Either encrypt/decrypt it on the client side, or use https(or user other cryptographic means to secure connection between server and client).

密码可以通过哪些方式被窃取和解密?我需要注意什么?这取决于所使用的算法。但是,请始终确保您不会向用户或从用户发送未加密的密码。要么在客户端对其进行加密/解密,要么使用 https(或用户其他加密方式来保护服务器和客户端之间的连接)。

However if all you need is to store passwords in encrypted way, I would suggest you to use a simple XOR Cipher. The main problem with this algorithm is that it could be easily broken by frequency analysis. However as generally passwords are not made from long paragraphs of English text I don't think you should worry about it. The second problem with XOR Cipher is that if you have a message in both encrypted and decrypted form you could easily find out password with which it was encrypted. Again, not a big problem in your case as it only affects the user who already was compromised by other means.

但是,如果您只需要以加密方式存储密码,我建议您使用简单的 XOR 密码。该算法的主要问题是它很容易被频率分析破坏。然而,通常密码不是由长段英文文本组成的,我认为您不必担心。XOR Cipher 的第二个问题是,如果您有加密和解密两种形式的消息,您可以轻松找到加密的密码。同样,在您的情况下不是大问题,因为它只会影响已经通过其他方式受到损害的用户。

回答by Jon Rhoades

  1. The PHP function you are after is Mcrypt (http://www.php.net/manual/en/intro.mcrypt.php).
  1. 您所追求的 PHP 函数是 Mcrypt ( http://www.php.net/manual/en/intro.mcrypt.php)。

The example from the manual is slightly edited for this example):

对于本示例,手册中的示例略有编辑):

<?php
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "This is a very secret key";
$pass = "PasswordHere";
echo strlen($pass) . "\n";

$crypttext = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $pass, MCRYPT_MODE_ECB, $iv);
echo strlen($crypttext) . "\n";
?>

You would use mcrypt_decryptto decrypt your password.

您将使用mcrypt_decrypt来解密您的密码。

  1. The best algorithmis rather subjective - ask 5 people, get 5 answers. Personally if the the default (Blowfish) isn't good enough for you, you probably have bigger problems!

  2. Given that it is needed by PHP to encrypt - not sure you can hide it anywhere - welcome comments on this. Standard PHP best coding practices apply of course!

  3. Given that the encryption key will be in your code anyway, not sure what you will gain, providing the rest of your application is secure.

  4. Obviously, if the encrypted password and the encryption key are stolen, then game over.

  1. 最好的算法是相当主观的——问 5 个人,得到 5 个答案。就个人而言,如果默认的 (Blowfish) 对您来说不够好,那么您可能会遇到更大的问题!

  2. 鉴于 PHP 需要加密 - 不确定您可以将它隐藏在任何地方 - 欢迎对此发表评论。标准 PHP 最佳编码实践当然适用!

  3. 鉴于加密密钥无论如何都会在您的代码中,不确定您会获得什么,前提是您的应用程序的其余部分是安全的。

  4. 显然,如果加密的密码和加密密钥被盗,那么游戏就结束了。

I'd put a rider on my answer - I'm not a PHP crypto expert, but, I think what I have answered is standard practice - I welcome comments other may have.

我会在我的答案上加一个骑手 - 我不是 PHP 加密专家,但是,我认为我的回答是标准做法 - 我欢迎其他人的评论。

回答by Bradley

A lot of users have suggested using mcrypt... which is correct, but I like to go a step further to make it easily stored and transfered (as sometimes encrypted values can make them hard to send using other technologies like curl, or json).

许多用户建议使用 mcrypt...这是正确的,但我更喜欢更进一步,使其易于存储和传输(因为有时加密的值会使它们难以使用 curl 或 json 等其他技术发送) .

After you have successfully encrypted using mcrypt, run it through base64_encode and then convert it to hex code. Once in hex code it's easy to transfer in a variety of ways.

使用 mcrypt 成功加密后,通过 base64_encode 运行它,然后将其转换为十六进制代码。一旦进入十六进制代码,就很容易以各种方式传输。

$td = mcrypt_module_open('tripledes', '', 'ecb', '');
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$key = substr("SUPERSECRETKEY",0,mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$encrypted = mcrypt_generic($td, $unencrypted);
$encrypted = $ua."||||".$iv;
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$encrypted = base64_encode($encrypted);
$encrypted = array_shift(unpack('H*', $encrypted));

And on the other side:

另一方面:

$encrypted = pack('H*', $encrypted);
$encrypted = base64_decode($encrypted);
list($encrypted,$iv) = explode("||||",$encrypted,2);
$td = mcrypt_module_open('tripledes', '', 'ecb', '');
$key = substr("SUPERSECRETKEY",0,mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$unencrypted = mdecrypt_generic($td, $encrypted);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);

回答by Long Ears

I'd only suggest public key encryption if you want the ability to set a user's password without their interaction (this can be handy for resets and shared passwords).

如果您希望能够在没有用户交互的情况下设置用户密码(这对于重置和共享密码很方便),我只建议使用公钥加密。

Public key

公钥

  1. The OpenSSLextension, specifically openssl_public_encryptand openssl_private_decrypt
  2. This would be straight RSA assuming your passwords will fit in key size - padding, otherwise you need a symmetric layer
  3. Store both keys for each user, the private key's passphrase is their application password
  1. OpenSSL的扩展,特别是openssl_public_encryptopenssl_private_decrypt
  2. 假设您的密码适合密钥大小 - 填充,这将是直接的 RSA,否则您需要一个对称层
  3. 为每个用户存储两个密钥,私钥的密码是他们的应用程序密码

Symmetric

对称

  1. The Mcryptextension
  2. AES-256 is probably a safe bet, but this could be a SO question in itself
  3. You don't - this would be their application password
  1. 这个Mcrypt扩展
  2. AES-256 可能是一个安全的赌注,但这本身可能是一个 SO 问题
  3. 你没有 - 这将是他们的应用程序密码

Both

两个都

4. Yes - users would have to enter their application password every time, but storing it in the session would raise other issues

4. 是的 - 用户每次都必须输入他们的应用程序密码,但将其存储在会话中会引发其他问题

5.

5.

  • If someone steals the application data, it's as secure as the symmetric cipher (for the public key scheme, it's used to protect the private key with the passphrase.)
  • Your application should definitely be only accessible over SSL, preferably using client certificates.
  • Consider adding a second factor for authentication which would only be used once per session, like a token sent via SMS.
  • 如果有人窃取了应用程序数据,它与对称密码一样安全(对于公钥方案,它用于通过密码保护私钥。)
  • 您的应用程序绝对应该只能通过 SSL 访问,最好使用客户端证书。
  • 考虑添加第二个身份验证因素,每个会话仅使用一次,例如通过 SMS 发送的令牌。

回答by Santosh Linkha

I tried something like this but please note that I am not cryptographer nor I hold in-depth knowledge about phpor any programming language. It's just an idea. My idea is to store keyin some file or database(or enter manually) which(location) cannot be easily predicted(And of course anything will be decrypted someday, the concept is to lengthen the decryption time) and encrypt sensitive information.

我尝试过这样的事情,但请注意,我不是密码学家,也不是php任何编程语言的深入知识。这只是一个想法。我的想法是存储key在某个文件中或database(或手动输入)无法轻易预测的(位置)(当然任何东西总有一天会被解密,其概念是延长解密时间)并加密敏感信息。

$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH , MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "evenifyouaccessmydatabaseyouwillneverfindmyemail";
$text = "[email protected]";
echo "Key : ".$key."<br/>";
echo "Text : ".$text . "<br/>";
echo "Md5 : ".md5($text). "<br/>";
echo "Sha1 : ".sha1($text). "<br/>";



$crypttext = mcrypt_encrypt(MCRYPT_BLOWFISH , $key, $text, MCRYPT_MODE_ECB, $iv);
echo "Crypted Data : ".$crypttext."<br>";

$base64 = base64_encode($crypttext);
echo "Encoded Data : ".$base64."<br/>";
$decode =  base64_decode($base64);


$decryptdata = mcrypt_decrypt(MCRYPT_BLOWFISH , $key, $crypttext, MCRYPT_MODE_ECB, $iv);

echo "Decoded Data : ".ereg_replace("?", null ,  $decryptdata); 
//event if i add '?' to the sting to the text it works, I don't know why.

Please note that it is just a concept. Any improvement on this code would be highly appreciable.

请注意,这只是一个概念。对此代码的任何改进都将是非常可观的。

回答by symcbean

The passwords are for a hardware device, so checking against hashes are out of the question

密码是针对硬件设备的,因此检查哈希值是不可能的

Eh? I don't understand. Do you just mean that password must be recoverable?

诶?我不明白。你只是说密码必须是可恢复的吗?

As others have said, the mcrypt extension provides access to lots of cryptographic functions - however you are inviting your users to put all their eggs in one basket - one which will be potentially be a target for attackers - and if you don't even know how to start solving the problem then you are doing your users a disservice. You are not in a position to understand how to protect the data.

正如其他人所说,mcrypt 扩展提供了对许多加密功能的访问——但是你邀请你的用户把他们所有的鸡蛋放在一个篮子里——一个可能成为攻击者目标的篮子——如果你甚至不知道如何开始解决问题,那么你就是在伤害你的用户。您无法了解如何保护数据。

Most security vulnerabilities come about not because the underlying algorithm is flawed or insecure - but because of problems with the way the algorithm is used within the application code.

大多数安全漏洞并不是因为底层算法有缺陷或不安全——而是因为算法在应用程序代码中的使用方式存在问题。

Having said that, it is possibleto build a reasonably secure system.

话虽如此,建立一个合理安全的系统是可能的

You should only consider asymmetric encryption if you have a requirement for a user to create a secure message which is readable by another (specific) user. The reason being that its computationally expensive. If you just want to provide a repository for users to enter and retrieve their own data, symmetric encryption is adequate.

如果您要求用户创建可由另一个(特定)用户读取的安全消息,您应该只考虑非对称加密。原因是它的计算成本很高。如果您只想为用户提供一个存储库来输入和检索他们自己的数据,对称加密就足够了。

If, however, you store the key for decrypting the message in the same place as the encrypted message (or where the encrypted message is stored) then the system is not secure. Use the same token for authenticating the user as for the decryption key (or in the case of assymetric encryption, use the token as the private key pass phrase). Since you will need to store the token on the server where the decryption takes place at least temporarily, you might want to consider using a non-searchable session storage substrate, or passing the token directly to a daemon associated with the session which would store the token in memory and perform the decryption of messages on demand.

但是,如果您将用于解密消息的密钥存储在与加密消息相同的位置(或存储加密消息的位置),那么系统就不安全。使用与解密密钥相同的令牌来验证用户(或者在非对称加密的情况下,使用令牌作为私钥密码短语)。由于您需要至少暂时将令牌存储在进行解密的服务器上,因此您可能需要考虑使用不可搜索的会话存储基板,或将令牌直接传递给与会话相关联的守护进程,该守护进程将存储内存中的令牌并根据需要执行消息解密。

回答by jvitoroc

Use password_hashand password_verify

使用password_hashpassword_verify

<?php
/**
 * In this case, we want to increase the default cost for BCRYPT to 12.
 * Note that we also switched to BCRYPT, which will always be 60 characters.
 */
$options = [
    'cost' => 12,
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
?>

And to decrypt:

并解密:

<?php
// See the password_hash() example to see where this came from.
$hash = 'y$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}
?>