双向加密:我需要存储可以检索的密码

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

我需要知道的是:

  • 我如何在PHP中加密和解密密码?

  • 什么是最安全的算法来加密密码?

  • 我在哪里存储私钥?

  • 而不是存储私钥,是否需要用户在需要解密密码时输入私钥? (这个应用程序的用户可以信任)

  • 密码可以以什么方式被窃取和解密? 我需要注意什么?


  • 就个人而言,我会像其他人一样使用mcrypt 。 但还有很多值得注意的地方......

  • 我如何在PHP中加密和解密密码?

    请参阅以下内容,了解为您提供一切服务的强大课程:

  • 什么是最安全的算法来加密密码?

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

  • 我在哪里存储私钥?

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

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

    这样做的好处是,任何2个密钥都可以在数据不受影响的情况下受到妥协。 如果存在SQL注入攻击,他们可以获取$userKey ,但不能获得其他2.如果存在本地服务器漏洞,他们可以获取$userKey$serverKey ,但不会获得第三个$userSuppliedKey 。 如果他们用扳手击败了用户,他们可以获得$userSuppliedKey ,但不能获得其他2个(但是如果用户用扳手击打,则无论如何也不会太迟)。

  • 而不是存储私钥,是否需要用户在需要解密密码时输入私钥? (这个应用程序的用户可以信任)

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

  • 密码可以以什么方式被窃取和解密? 我需要注意什么?

    任何形式的妥协都会让您查看加密数据。 如果他们可以注入代码或进入文件系统,他们可以查看解密的数据(因为他们可以编辑解密数据的文件)。 任何形式的重播或MITM攻击都可以让他们完全访问所涉及的密钥。 嗅探原始的HTTP流量也会给他们提供密钥。

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

  • 编辑:下面是一个强大的加密方法的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);
        }
    }
    

    请注意,我正在使用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);
    }
    

    用法:

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

    然后,解密:

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

    请注意,我第二次使用$e2来向您显示不同的实例仍将正确解密数据。

    现在,它是如何工作的/为什么将它用于另一种解决方案:

  • 按键

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

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

  • 重要提示$rounds参数配置为具有足够强度的真正随机密钥(至少128位密码安全随机密钥)。 如果您要使用密码或非随机密钥(或随机选择较小的CS随机128位),则必须增加此参数。 我会建议至少10000个密码(越多越好,但它会增加运行时间)...

  • 数据的完整性

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

  • 它使用mcrypt来实际执行加密。 我建议使用MCRYPT_BLOWFISHMCRYPT_RIJNDAEL_128密码和MCRYPT_MODE_CBC作为模式。 它足够强大,而且速度还相当快(我的机器上的加密和解密周期大约需要1/2秒)。
  • 现在,就第一个列表中的第3点而言,会给你的是一个这样的函数:

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

    你可以在makeKey()函数中扩展它,但由于它将在稍后延伸,所以这并不是一个真正的重点。

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

  • 盐的16字节
  • hmac有64个字节
  • 数据长度
  • 填充使数据长度%8 == 0
  • 因此,对于16个字符的数据源,将会有16个字符的数据进行加密。 这意味着由于填充,实际的加密数据大小为16个字节。 然后添加salt的16个字节和hmac的64个字节,总存储大小为96个字节。 所以最多只有80个字符的开销,最坏的是87个字符的开销......

    我希望这有助于...

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


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

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

    我在哪里存储私钥? 如果您决定实施公钥 - 加密算法(例如RSA),则不存储私钥。 用户有私钥。 你的系统有公钥,可以存储在任何你想要的地方。

    而不是存储私钥,是否需要用户在需要解密密码时输入私钥? (这个应用程序的用户可以信任)那么如果你的用户能够记得可笑的长素数然后 - 是的,为什么不呢。 但通常你需要想出一个系统,它允许用户将密钥存储在某个地方。

    密码可以以什么方式被窃取和解密? 我需要注意什么? 这取决于使用的算法。 但是,请务必确保不向用户发送未加密的密码。 无论是在客户端加密/解密,还是使用https(或用户其他加密手段来保护服务器和客户端之间的连接)。

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


  • 您所使用的PHP函数是Mcrypt(http://www.php.net/manual/en/intro.mcrypt.php)。
  • 本例稍微修改了手册中的例子):

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

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

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

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

  • 鉴于无论如何都会在您的代码中加密密钥,不确定您将获得什么,因为提供其他应用程序是安全的。

  • 显然,如果加密密码和加密密钥被盗,那么游戏结束。

  • 我会在我的答案中加上一个骑手 - 我不是PHP加密专家,但我认为我的回答是标准实践 - 我欢迎其他人可能会发表的评论。

    链接地址: http://www.djcxy.com/p/28939.html

    上一篇: way encryption: I need to store passwords that can be retrieved

    下一篇: Best way to use PHP to encrypt and decrypt passwords?