【问题标题】:How to use bcrypt in mysql with php如何通过 php 在 mysql 中使用 bcrypt
【发布时间】:2020-11-05 20:48:25
【问题描述】:

我想在 MySQL 中加密/保护我的 phoneNumber 列,因此为此我使用了 bcrypt 但无法解密这个

这是我的代码

$password="abcd";
echo $pwd=password_hash($password, PASSWORD_DEFAULT);   //$2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

现在我想 dcrypt,所以我尝试使用以下代码,但显示“无效密码”,我错在哪里?

if (password_verify($password, $pwd)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}

【问题讨论】:

  • 上面的代码可以正常工作 - 它确实验证了给定的密码
  • @ProfessorAbronsius:如果上面的代码是正确的,那么我们如何解密并传递数据?
  • 它不是 encryption 本身 - 它是 hashing - 但您可以 hash 电话号码并验证提供的电话号码是否有效,但您不能对哈希进行逆向工程以返回原来的电话号码。您的代码中的BCRYPT 在哪里?
  • 您无法解密 bcrypt 哈希。您只能检查原始明文密码是否与哈希相对应。如果你想解密一个 bcrypt 哈希,你必须暴力破解它,这需要很长时间。
  • @ProfessorAbronsius:我想发送数据(带电话号码)所以为此我应该怎么做?应该使用哪种类型的加密/安全?

标签: php mysql encryption bcrypt


【解决方案1】:

一个非常基本的加密类,如下所示,将允许您加密和解密数据 - 加密的数据可以存储在数据库中或通过电子邮件等发送 - 没有承诺提供的安全级别但是因为很快就组装好了。

pubkeyhashsecret 越长越好...

<?php

    class encryption{
        
        public function __construct(){
            $this->config=array(
                'hashalgo'      =>  'sha256',
                'cipher'        =>  'AES-128-CBC',
                'hashsecret'    =>  'banana womble hippopotamus pomegranate',
                'pubkey'        =>  'CAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv
YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
bjCCASIwDQYJKoZIhvcNAQEBBQADggEPA'
            );
        }
        
        private function makehash( $data, $key ){
            return hash_hmac( $this->config['hashalgo'], $data, $key, true );
        }
    
        public function encrypt( $data=false ){
            $conf=(object)$this->config;
            
            if( !empty( $data ) && in_array( $conf->cipher, openssl_get_cipher_methods() ) ){
                
                $ivlen = openssl_cipher_iv_length( $conf->cipher );
                $iv = openssl_random_pseudo_bytes( $ivlen );
                
                $encrypted = openssl_encrypt( $data, $conf->cipher, $conf->pubkey, $options=OPENSSL_RAW_DATA, $iv );
                $hash = $this->makehash( $encrypted, $conf->pubkey );
                
                return base64_encode( $iv . $hash . $encrypted );
            }
            return false;
        }
        
        public function decrypt( $data ){
            $conf=(object)$this->config;
            if( !empty( $data ) && in_array( $conf->cipher, openssl_get_cipher_methods() ) ){
                
                $shalength=32;
                $data = base64_decode( $data );
                $ivlen = openssl_cipher_iv_length( $conf->cipher );
                $iv = substr( $data, 0, $ivlen );
                $hash = substr( $data, $ivlen, $shalength );
                
                $encrypted = substr( $data, $ivlen + $shalength );
                $decrypted = openssl_decrypt( $encrypted, $conf->cipher, $conf->pubkey, $options=OPENSSL_RAW_DATA, $iv );
                
                if( $decrypted && hash_equals( $hash, $this->makehash( $encrypted, $conf->pubkey ) ) ){
                    return $decrypted;
                }
            }
            return false;
        }   
    }
    
    
    
    $obj=new encryption;
    
    
    $password="pocahontas";
    $encrypted=$obj->encrypt( $password );
    $decrypted=$obj->decrypt( $encrypted );
    
    printf('<pre>
    Password: %s
    Encrypted: %s
    Decrypted: %s
    </pre>',$password,$encrypted,$decrypted);

?>

这将输出如下内容:

Password: pocahontas
Encrypted: Hvx3j9lwtEII3pR+m05TYDv+BV0IwLa8dQavGgaeQCJITSCU88AYhZrt+swMYNKOD3VQX6PN0mcA/rDkdwmqGw==
Decrypted: pocahontas

如您所见,原始密码每次都使用不同的值加密,但解密时总是解码为相同的原始密码。

【讨论】:

    【解决方案2】:

    正如其他 cmets 所述,bcrypt 方法适用于比较密码(“密码是否正确?”),但不能作为加密/解密的来源。

    对于次要安全级别(保存电话号码),我相信密钥派生方法(此处为 PBKDF2)和 ECB 模式下的 AES 的组合是可以的,因为当数据被保存时,ECB 模式变得不安全大于 16 字节的块长度(大多数电话号码应该适合 16 的长度:-)。如果您需要更安全的级别,您应该考虑使用 GCM 模式,但那将是另一个问题。

    我的代码在一个 PHP 类中模拟加密和稍后的解密;通常这将在单独的类中完成,因此我使用“加倍”变量名(例如'salt'和'saltToDecrypt')并且它使用可用的openssl库 几乎所有现代 PHP 实现(我使用的是 PHP 7.3)。

    代码中充满了“证明”加密侧的变量与解密侧的变量相同的回声,它们可以在生产中被注释掉。

    该代码不做任何错误处理(例如,在解密端输入错误的密码)并且仅用于教育。

    结果将显示正确的解密,并且由于“salt”中的随机元素,程序的每次运行(具有相同的明文和密码)都会获得不同的编码字符串。

    这是输出:

    Encryption with AES ECB mode and PBKDF2 key derivation<br>
    plaintext              : 0123-45678-90123<br>
    password               : myPassword<br>
    salt                   : 2dccc75991638322d6b1954726f969bcbfc86b00e9928a759c0ea9618eedd4f8<br>
    hash                   : a86178ca7aebf3cde4b8472ac5825cea<br>
    ecbEncrypt             : NWt6Cyi11m7Vzwm1tsRhE6l8S5IH8Ko9HIgYeKg7yFQ=<br>
    token                  : Tld0NkN5aTExbTdWendtMXRzUmhFNmw4UzVJSDhLbzlISWdZZUtnN3lGUT06Oi3Mx1mRY4Mi1rGVRyb5aby/yGsA6ZKKdZwOqWGO7dT4<br>
    
    Decryption
    passwordToDecrypt      : myPassword<br>
    dataToDecrypt          : NWt6Cyi11m7Vzwm1tsRhE6l8S5IH8Ko9HIgYeKg7yFQ=<br>
    saltToDecrypt          : 2dccc75991638322d6b1954726f969bcbfc86b00e9928a759c0ea9618eedd4f8<br>
    hashToDecrypt          : a86178ca7aebf3cde4b8472ac5825cea<br>
    decrypted plaintext    : 0123-45678-90123<br>
    

    代码:

    <?php
    echo PHP_EOL . "Encryption with AES ECB mode and PBKDF2 key derivation" . '<br>' . PHP_EOL;
    // https://stackoverflow.com/questions/62930372/how-to-use-bcrypt-in-mysql-with-php/62931080#62931080
    
    $plaintext = "0123-45678-90123"; // telephone number
    $password = "myPassword";
    echo "plaintext              : " . $plaintext . '<br>' . PHP_EOL;
    echo "password               : " . $password . '<br>' . PHP_EOL;
    $iterations = 10000; // better to use higher iterations but this slows down the process (but this is wanted !)
    
    // generate password hash with pbkdf2 and encrypt
    $salt = openssl_random_pseudo_bytes(32);
    $hash = hash_pbkdf2("sha256", $password, $salt, $iterations, 32);
    echo "salt                   : " . bin2hex($salt) . '<br>' . PHP_EOL;
    echo "hash                   : " . $hash . '<br>' . PHP_EOL;
    $ecbEncrypt = openssl_encrypt($plaintext, "aes-256-ecb", $hash, $options=0);
    echo "ecbEncrypt             : " . $ecbEncrypt . '<br>' . PHP_EOL;
    $token = base64_encode($ecbEncrypt . '::' . $salt);
    echo "token                  : " . $token . '<br>' . PHP_EOL;
    // save this token in the database
    
    // load the token from the database and decrypt
    echo PHP_EOL . "Decryption" . PHP_EOL;
    $passwordToDecrypt = "myPassword";
    $iterationsToDecrypt = 10000; // better to use higher iterations but this slows down the process (but this is wanted !)
    $tokenLoad = $token;
    list($dataToDecrypt, $saltToDecrypt) = explode('::', base64_decode($tokenLoad), 2);
    echo "passwordToDecrypt      : " . $passwordToDecrypt . '<br>' . PHP_EOL;
    echo "dataToDecrypt          : " . $dataToDecrypt . '<br>' . PHP_EOL;
    echo "saltToDecrypt          : " . bin2hex($saltToDecrypt) . '<br>' . PHP_EOL;
    $hashToDecrypt = hash_pbkdf2("sha256", $passwordToDecrypt, $saltToDecrypt, $iterationsToDecrypt, 32);
    echo "hashToDecrypt          : " . $hashToDecrypt. '<br>' . PHP_EOL;
    $ecbDecrypt = openssl_decrypt($dataToDecrypt, "aes-256-ecb", $hashToDecrypt, $options=0);
    echo "decrypted plaintext    : " . $ecbDecrypt . '<br>' . PHP_EOL;
    ?>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-14
      • 2018-09-03
      • 1970-01-01
      • 2019-05-29
      • 2011-10-16
      • 2012-09-22
      • 2020-10-24
      • 2013-08-09
      相关资源
      最近更新 更多