2017-02-10 168 views
1

我正在Android上創建一個基本的登錄和註冊應用程序。無法比較/解密密碼(PHP/Android)

我目前面臨的問題是我可以加密密碼(如圖所示),但是當我嘗試在我的Android應用程序中登錄時,它失敗。

我試圖做一個非加密的登錄,它的工作原理。

我的理論是,PHP文件無法比較或解密密碼哈希?

下面的代碼和截圖。感謝您的時間!

Screenshot of the database

enter image description here


的login.php

<?php 
require("password.php"); 

$con = mysqli_connect("?", "?", "?", "?"); 

$username = $_POST["username"]; 
$password = $_POST["password"]; 

$statement = mysqli_prepare($con, "SELECT * FROM user WHERE username = ?"); 
mysqli_stmt_bind_param($statement, "s", $username); 
mysqli_stmt_execute($statement); 
mysqli_stmt_store_result($statement); 
mysqli_stmt_bind_result($statement, $colUserID, $colName, $colUsername, $colAge, $colPassword); 

$response = array(); 
$response["success"] = false; 

while(mysqli_stmt_fetch($statement)){ 
    if (password_verify($password, $colPassword)) { 
     $response["success"] = true; 
     $response["name"] = $colName; 
     $response["age"] = $colAge; 
    } 
} 

echo json_encode($response); ?> 

Register.php

<?php 
require("password.php"); 

$connect = mysqli_connect("?", "?", "?", "?"); 

$name = $_POST["name"]; 
$age = $_POST["age"]; 
$username = $_POST["username"]; 
$password = $_POST["password"]; 

function registerUser() { 
    global $connect, $name, $age, $username, $password; 
    $passwordHash = password_hash($password, PASSWORD_DEFAULT); 
    $statement = mysqli_prepare($connect, "INSERT INTO user (name, age, username, password) VALUES (?, ?, ?, ?)"); 
    mysqli_stmt_bind_param($statement, "siss", $name, $age, $username, $passwordHash); 
    mysqli_stmt_execute($statement); 
    mysqli_stmt_close($statement);  
} 

function usernameAvailable() { 
    global $connect, $username; 
    $statement = mysqli_prepare($connect, "SELECT * FROM user WHERE username = ?"); 
    mysqli_stmt_bind_param($statement, "s", $username); 
    mysqli_stmt_execute($statement); 
    mysqli_stmt_store_result($statement); 
    $count = mysqli_stmt_num_rows($statement); 
    mysqli_stmt_close($statement); 
    if ($count < 1){ 
     return true; 
    }else { 
     return false; 
    } 
} 

$response = array(); 
$response["success"] = false; 

if (usernameAvailable()){ 
    registerUser(); 
    $response["success"] = true; 
} 

echo json_encode($response); ?> 

password.php

<?php namespace { 

if (!defined('PASSWORD_BCRYPT')) { 
    /** 
    * PHPUnit Process isolation caches constants, but not function declarations. 
    * So we need to check if the constants are defined separately from 
    * the functions to enable supporting process isolation in userland 
    * code. 
    */ 
    define('PASSWORD_BCRYPT', 1); 
    define('PASSWORD_DEFAULT', PASSWORD_BCRYPT); 
    define('PASSWORD_BCRYPT_DEFAULT_COST', 10); 
} 

if (!function_exists('password_hash')) { 

    /** 
    * Hash the password using the specified algorithm 
    * 
    * @param string $password The password to hash 
    * @param int $algo  The algorithm to use (Defined by PASSWORD_* constants) 
    * @param array $options The options for the algorithm to use 
    * 
    * @return string|false The hashed password, or false on error. 
    */ 
    function password_hash($password, $algo, array $options = array()) { 
     if (!function_exists('crypt')) { 
      trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING); 
      return null; 
     } 
     if (is_null($password) || is_int($password)) { 
      $password = (string) $password; 
     } 
     if (!is_string($password)) { 
      trigger_error("password_hash(): Password must be a string", E_USER_WARNING); 
      return null; 
     } 
     if (!is_int($algo)) { 
      trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING); 
      return null; 
     } 
     $resultLength = 0; 
     switch ($algo) { 
      case PASSWORD_BCRYPT: 
       $cost = PASSWORD_BCRYPT_DEFAULT_COST; 
       if (isset($options['cost'])) { 
        $cost = (int) $options['cost']; 
        if ($cost < 4 || $cost > 31) { 
         trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING); 
         return null; 
        } 
       } 
       // The length of salt to generate 
       $raw_salt_len = 16; 
       // The length required in the final serialization 
       $required_salt_len = 22; 
       $hash_format = sprintf("$2y$%02d$", $cost); 
       // The expected length of the final crypt() output 
       $resultLength = 60; 
       break; 
      default: 
       trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING); 
       return null; 
     } 
     $salt_req_encoding = false; 
     if (isset($options['salt'])) { 
      switch (gettype($options['salt'])) { 
       case 'NULL': 
       case 'boolean': 
       case 'integer': 
       case 'double': 
       case 'string': 
        $salt = (string) $options['salt']; 
        break; 
       case 'object': 
        if (method_exists($options['salt'], '__tostring')) { 
         $salt = (string) $options['salt']; 
         break; 
        } 
       case 'array': 
       case 'resource': 
       default: 
        trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING); 
        return null; 
      } 
      if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) { 
       trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING); 
       return null; 
      } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) { 
       $salt_req_encoding = true; 
      } 
     } else { 
      $buffer = ''; 
      $buffer_valid = false; 
      if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) { 
       $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM); 
       if ($buffer) { 
        $buffer_valid = true; 
       } 
      } 
      if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) { 
       $strong = false; 
       $buffer = openssl_random_pseudo_bytes($raw_salt_len, $strong); 
       if ($buffer && $strong) { 
        $buffer_valid = true; 
       } 
      } 
      if (!$buffer_valid && @is_readable('/dev/urandom')) { 
       $file = fopen('/dev/urandom', 'r'); 
       $read = 0; 
       $local_buffer = ''; 
       while ($read < $raw_salt_len) { 
        $local_buffer .= fread($file, $raw_salt_len - $read); 
        $read = PasswordCompat\binary\_strlen($local_buffer); 
       } 
       fclose($file); 
       if ($read >= $raw_salt_len) { 
        $buffer_valid = true; 
       } 
       $buffer = str_pad($buffer, $raw_salt_len, "\0")^str_pad($local_buffer, $raw_salt_len, "\0"); 
      } 
      if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) { 
       $buffer_length = PasswordCompat\binary\_strlen($buffer); 
       for ($i = 0; $i < $raw_salt_len; $i++) { 
        if ($i < $buffer_length) { 
         $buffer[$i] = $buffer[$i]^chr(mt_rand(0, 255)); 
        } else { 
         $buffer .= chr(mt_rand(0, 255)); 
        } 
       } 
      } 
      $salt = $buffer; 
      $salt_req_encoding = true; 
     } 
     if ($salt_req_encoding) { 
      // encode string with the Base64 variant used by crypt 
      $base64_digits = 
       'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/'; 
      $bcrypt64_digits = 
       './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 

      $base64_string = base64_encode($salt); 
      $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits); 
     } 
     $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len); 

     $hash = $hash_format . $salt; 

     $ret = crypt($password, $hash); 

     if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) { 
      return false; 
     } 

     return $ret; 
    } 

    /** 
    * Get information about the password hash. Returns an array of the information 
    * that was used to generate the password hash. 
    * 
    * array(
    * 'algo' => 1, 
    * 'algoName' => 'bcrypt', 
    * 'options' => array(
    *  'cost' => PASSWORD_BCRYPT_DEFAULT_COST, 
    * ), 
    *) 
    * 
    * @param string $hash The password hash to extract info from 
    * 
    * @return array The array of information about the hash. 
    */ 
    function password_get_info($hash) { 
     $return = array(
      'algo' => 0, 
      'algoName' => 'unknown', 
      'options' => array(), 
     ); 
     if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) { 
      $return['algo'] = PASSWORD_BCRYPT; 
      $return['algoName'] = 'bcrypt'; 
      list($cost) = sscanf($hash, "$2y$%d$"); 
      $return['options']['cost'] = $cost; 
     } 
     return $return; 
    } 

    /** 
    * Determine if the password hash needs to be rehashed according to the options provided 
    * 
    * If the answer is true, after validating the password using password_verify, rehash it. 
    * 
    * @param string $hash The hash to test 
    * @param int $algo The algorithm used for new password hashes 
    * @param array $options The options array passed to password_hash 
    * 
    * @return boolean True if the password needs to be rehashed. 
    */ 
    function password_needs_rehash($hash, $algo, array $options = array()) { 
     $info = password_get_info($hash); 
     if ($info['algo'] !== (int) $algo) { 
      return true; 
     } 
     switch ($algo) { 
      case PASSWORD_BCRYPT: 
       $cost = isset($options['cost']) ? (int) $options['cost'] : PASSWORD_BCRYPT_DEFAULT_COST; 
       if ($cost !== $info['options']['cost']) { 
        return true; 
       } 
       break; 
     } 
     return false; 
    } 

    /** 
    * Verify a password against a hash using a timing attack resistant approach 
    * 
    * @param string $password The password to verify 
    * @param string $hash  The hash to verify against 
    * 
    * @return boolean If the password matches the hash 
    */ 
    function password_verify($password, $hash) { 
     if (!function_exists('crypt')) { 
      trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING); 
      return false; 
     } 
     $ret = crypt($password, $hash); 
     if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) { 
      return false; 
     } 

     $status = 0; 
     for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) { 
      $status |= (ord($ret[$i])^ord($hash[$i])); 
     } 

     return $status === 0; 
    } 
} } namespace PasswordCompat\binary { 

if (!function_exists('PasswordCompat\\binary\\_strlen')) { 

    /** 
    * Count the number of bytes in a string 
    * 
    * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension. 
    * In this case, strlen() will count the number of *characters* based on the internal encoding. A 
    * sequence of bytes might be regarded as a single multibyte character. 
    * 
    * @param string $binary_string The input string 
    * 
    * @internal 
    * @return int The number of bytes 
    */ 
    function _strlen($binary_string) { 
     if (function_exists('mb_strlen')) { 
      return mb_strlen($binary_string, '8bit'); 
     } 
     return strlen($binary_string); 
    } 

    /** 
    * Get a substring based on byte limits 
    * 
    * @see _strlen() 
    * 
    * @param string $binary_string The input string 
    * @param int $start 
    * @param int $length 
    * 
    * @internal 
    * @return string The substring 
    */ 
    function _substr($binary_string, $start, $length) { 
     if (function_exists('mb_substr')) { 
      return mb_substr($binary_string, $start, $length, '8bit'); 
     } 
     return substr($binary_string, $start, $length); 
    } 

    /** 
    * Check if current PHP version is compatible with the library 
    * 
    * @return boolean the check result 
    */ 
    function check() { 
     static $pass = NULL; 

     if (is_null($pass)) { 
      if (function_exists('crypt')) { 
       $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG'; 
       $test = crypt("password", $hash); 
       $pass = $test == $hash; 
      } else { 
       $pass = false; 
      } 
     } 
     return $pass; 
    } 

}} 
+0

仍在尋找答案。謝謝。 –

回答

1

河豚一般是一個更好的散列算法和下面的代碼更容易實現(來源:http://php.net/manual/en/function.crypt.php

<?php 

function password_hash($password, $cost=11){ 
     /* To generate the salt, first generate enough random bytes. Because 
     * base64 returns one character for each 6 bits, the we should generate 
     * at least 22*6/8=16.5 bytes, so we generate 17. Then we get the first 
     * 22 base64 characters 
     */ 
     $salt=substr(base64_encode(openssl_random_pseudo_bytes(17)),0,22); 
     /* As blowfish takes a salt with the alphabet ./A-Za-z0-9 we have to 
     * replace any '+' in the base64 string with '.'. We don't have to do 
     * anything about the '=', as this only occurs when the b64 string is 
     * padded, which is always after the first 22 characters. 
     */ 
     $salt=str_replace("+",".",$salt); 
     /* Next, create a string that will be passed to crypt, containing all 
     * of the settings, separated by dollar signs 
     */ 
     $param='$'.implode('$',array(
       "2y", //select the most secure version of blowfish (>=PHP 5.3.7) 
       str_pad($cost,2,"0",STR_PAD_LEFT), //add the cost in two digits 
       $salt //add the salt 
     )); 

     //now do the actual hashing 
     return crypt($password,$param); 
} 

/* 
* Check the password against a hash generated by the generate_hash 
* function. 
*/ 
function password_verify($password, $hash){ 
     /* Regenerating the with an available hash as the options parameter should 
     * produce the same hash if the same password is passed. 
     */ 
     return crypt($password, $hash)==$hash; 
} 
?> 
+0

此外,你是正確的,「PHP文件無法解密密碼哈希」,但我不確定爲什麼,只是上面的代碼對我更好。 – Dan

+0

那麼,我該如何正確實施代碼呢?我應該用這個代碼替換我的password.php嗎? @Dan –

+0

是的,只需用Blowfish代碼替換整個password.php代碼,我也會建議你在Register.php中改變這一行$ passwordHash = password_hash($ password,PASSWORD_DEFAULT);將PASSWORD_DEFAULT更改爲rand(),所以它應該如下所示:$ passwordHash = password_hash($ password,rand());這給了password_hash方法一個新的隨機起始號碼來創建新的哈希值。 – Dan

0

我有同樣的問題。 您的原始代碼是正確的。

只需將服務器上的密碼大小更改爲255個字符(實際上它應該是60個字符,建議使用255個字符)。