密码散列 SELECT (PHP)
Password Hashing SELECT (PHP)
是否可以仅使用发布的密码从 MySql 数据库中 select 散列和加盐密码?如果是,怎么做?
如果我这样散列密码:
$password = "blabla";
$hash = password_hash($password, PASSWORD_DEFAULT);
例如,$hash
将是 yzzd3lj6oIPlBPnCxsU7nOmtsEFlKw/BdqTXyMgbuojjVpiEe4rVm
,它将存储在数据库中。
在登录过程中,如果密码匹配,我如何只检查散列密码列,并且只检查 table 的列,只有 'blabla' 作为数据?
我想你的意思是如何从数据库中 select 散列和加盐的密码,然后用明文密码验证它?如果是这样,这里是使用 bcrypt 的方法。
请记住,这需要 PHP 5 >= 5.5.0.
此外,我建议 scrypt 而不是 bcrypt,但您必须手动安装 scrypt。
SQL东西
CREATE DATABASE `example`;
USE `example`;
CREATE TABLE `users` (
`id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(16),
`password` VARCHAR(255)
);
哈希 Class (classes/Hash.class.php)
<?php
class Hash
{
public static function make($string)
{
$options = array('cost' => 11);
return password_hash($string, PASSWORD_BCRYPT, $options)
}
public static function check($password, $hash)
{
return password_verify($password, $hash);
}
}
数据库Class (classes/DB.class.php)
<?php
class DB
{
private $dbhost = '127.0.0.1';
private $dbname = 'example';
private $dbuser = 'root';
private $dbpass = 'pass';
public function Connect()
{
return new PDO('mysql:host=' . $this->dbhost . ';dbname=' . $this->dbname, $this->dbuser, $this->pass);
}
}
用户 Class (classes/User.class.php)
<?php
require_once('DB.class.php');
require_once('Hash.class.php');
class User
{
private $db;
public function __construct()
{
$this->db = new DB();
$this->db = $this->db->Connect();
}
public function find($username)
{
$st = $this->db->prepare('SELECT * FROM `users` WHERE `username` = :username LIMIT 1');
$st->bindParam(':username', $username, PDO::PARAM_STR);
$st->execute();
if($st->rowCount())
{
return $st->fetch(PDO::FETCH_ASSOC);
}
return false;
}
public function create($username, $password)
{
$password = Hash::make($password);
$st = $this->db->prepare('INSERT INTO `users` (`username`, `password`) VALUES (:username, :password)');
$st->bindParam(':username', $username, PDO::PARAM_STR);
$st->bindParam(':password', $password, PDO::PARAM_STR);
$st->execute();
}
public function verify($username, $password)
{
$user = $this->find($username);
if($user)
{
if(Hash::check($password, $user['password']))
{
$_SESSION['isLoggedIn'] = true;
return true;
}
}
return false;
}
public function isLoggedIn()
{
if(isset($_SESSION['isLoggedIn']))
{
return true;
}
return false;
}
}
注册(register.php)
<?php
require_once('classes/User.class.php');
$user = new User();
if($user->isLoggedIn())
{
header('Location: index.php');
die();
}
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
$username = $_POST['username'];
$password = $_POST['password'];
// Check if username and password exist
if(!isset($username) || !isset($password))
{
die('Username and password required');
}
// Check if values are not empty
if(empty($username) || empty($password))
{
die('Blank fields not allowed');
}
// Check if username length is in between 4 and 16
if(strlen($username) < 4 && strlen($username) > 16)
{
die('Username must be in between 4 and 16 characters');
}
// Check if username is alphanumeric
if(!ctype_alnum($username))
{
die('Username must be alphanumeric');
}
// Check password length
if(strlen($password) < 8)
{
die('Passwords should be at least 8 characters long');
}
// Check if username exists
$exists = $user->find($username);
if($exists)
{
die('Username already in use');
}
// Create account
$user->create($username, $password);
header('Location: login.php');
die();
}
?>
// HTML goes here
登录(login.php)
<?php
require_once('classes/User.class.php');
$user = new User();
if($user->isLoggedIn())
{
header('Location: index.php');
die();
}
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
$username = $_POST['username'];
$password = $_POST['password'];
// Check if username and password exist
if(!isset($username) || !isset($password))
{
die('Username and password required');
}
// Check if values are not empty
if(empty($username) || empty($password))
{
die('Blank fields not allowed');
}
// Check if username length is in between 4 and 16
if(strlen($username) < 4 && strlen($username) > 16)
{
die('Username must be in between 4 and 16 characters');
}
// Check if username is alphanumeric
if(!ctype_alnum($username))
{
die('Username must be alphanumeric');
}
// Check password length
if(strlen($password) < 8)
{
die('Passwords should be at least 8 characters long');
}
// Try to login
$verified = $user->verify($username, $password);
if($verified)
{
header('Location: index.php');
die();
} else {
die('Invalid username/password');
}
}
?>
// HTML goes here
注销(logout.php)
<?php
require_once('classes/User.class.php');
$user = new User();
if($user->isLoggedIn())
{
unset($_SESSION['isLoggedIn']);
}
header('Location: login.php');
die();
索引 (index.php)
<?php
require_once('classes/User.class.php');
if(!$user->isLoggedIn())
{
header('Location: login.php');
die();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Welcome</title>
</head>
<body>
<h1>Menu</h1>
<ul>
<li><a href="logout.php">Logout?</a></li>
</ul>
</body>
</html>
How, during a login, do I check against the hashed password column only, and only table's column, if the passwords match, having only 'blabla' as data?
你不能。密码存储旨在使许多操作无法进行。如果您想在不使用用户名或其他密钥的情况下找到密码的匹配项,则需要对每个密码调用 password_verify
直到找到匹配项。按照设计,这将非常缓慢。
由于密码不需要是唯一的,您可能有一个密码可以匹配多个条目。
我猜这是个坏主意,不是您想要的。
无法使用数据库查询搜索正确加盐和散列的密码。您必须通过 username/email/... 搜索哈希,然后您可以使用找到的哈希验证输入的密码。
1) 首先查询存储的散列
SELECT passwordhash FROM users WHERE email = ?
2) 使用找到的哈希值验证输入的密码
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
是盐使搜索无法进行,必须先从存储的散列中提取盐,然后才能验证密码。这样的查询必须读取每个散列,提取其盐分并进行散列。因为散列函数非常慢(故意),查询将需要太长的时间才能执行。
是否可以仅使用发布的密码从 MySql 数据库中 select 散列和加盐密码?如果是,怎么做?
如果我这样散列密码:
$password = "blabla";
$hash = password_hash($password, PASSWORD_DEFAULT);
例如,$hash
将是 yzzd3lj6oIPlBPnCxsU7nOmtsEFlKw/BdqTXyMgbuojjVpiEe4rVm
,它将存储在数据库中。
在登录过程中,如果密码匹配,我如何只检查散列密码列,并且只检查 table 的列,只有 'blabla' 作为数据?
我想你的意思是如何从数据库中 select 散列和加盐的密码,然后用明文密码验证它?如果是这样,这里是使用 bcrypt 的方法。
请记住,这需要 PHP 5 >= 5.5.0.
此外,我建议 scrypt 而不是 bcrypt,但您必须手动安装 scrypt。
SQL东西
CREATE DATABASE `example`;
USE `example`;
CREATE TABLE `users` (
`id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(16),
`password` VARCHAR(255)
);
哈希 Class (classes/Hash.class.php)
<?php
class Hash
{
public static function make($string)
{
$options = array('cost' => 11);
return password_hash($string, PASSWORD_BCRYPT, $options)
}
public static function check($password, $hash)
{
return password_verify($password, $hash);
}
}
数据库Class (classes/DB.class.php)
<?php
class DB
{
private $dbhost = '127.0.0.1';
private $dbname = 'example';
private $dbuser = 'root';
private $dbpass = 'pass';
public function Connect()
{
return new PDO('mysql:host=' . $this->dbhost . ';dbname=' . $this->dbname, $this->dbuser, $this->pass);
}
}
用户 Class (classes/User.class.php)
<?php
require_once('DB.class.php');
require_once('Hash.class.php');
class User
{
private $db;
public function __construct()
{
$this->db = new DB();
$this->db = $this->db->Connect();
}
public function find($username)
{
$st = $this->db->prepare('SELECT * FROM `users` WHERE `username` = :username LIMIT 1');
$st->bindParam(':username', $username, PDO::PARAM_STR);
$st->execute();
if($st->rowCount())
{
return $st->fetch(PDO::FETCH_ASSOC);
}
return false;
}
public function create($username, $password)
{
$password = Hash::make($password);
$st = $this->db->prepare('INSERT INTO `users` (`username`, `password`) VALUES (:username, :password)');
$st->bindParam(':username', $username, PDO::PARAM_STR);
$st->bindParam(':password', $password, PDO::PARAM_STR);
$st->execute();
}
public function verify($username, $password)
{
$user = $this->find($username);
if($user)
{
if(Hash::check($password, $user['password']))
{
$_SESSION['isLoggedIn'] = true;
return true;
}
}
return false;
}
public function isLoggedIn()
{
if(isset($_SESSION['isLoggedIn']))
{
return true;
}
return false;
}
}
注册(register.php)
<?php
require_once('classes/User.class.php');
$user = new User();
if($user->isLoggedIn())
{
header('Location: index.php');
die();
}
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
$username = $_POST['username'];
$password = $_POST['password'];
// Check if username and password exist
if(!isset($username) || !isset($password))
{
die('Username and password required');
}
// Check if values are not empty
if(empty($username) || empty($password))
{
die('Blank fields not allowed');
}
// Check if username length is in between 4 and 16
if(strlen($username) < 4 && strlen($username) > 16)
{
die('Username must be in between 4 and 16 characters');
}
// Check if username is alphanumeric
if(!ctype_alnum($username))
{
die('Username must be alphanumeric');
}
// Check password length
if(strlen($password) < 8)
{
die('Passwords should be at least 8 characters long');
}
// Check if username exists
$exists = $user->find($username);
if($exists)
{
die('Username already in use');
}
// Create account
$user->create($username, $password);
header('Location: login.php');
die();
}
?>
// HTML goes here
登录(login.php)
<?php
require_once('classes/User.class.php');
$user = new User();
if($user->isLoggedIn())
{
header('Location: index.php');
die();
}
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
$username = $_POST['username'];
$password = $_POST['password'];
// Check if username and password exist
if(!isset($username) || !isset($password))
{
die('Username and password required');
}
// Check if values are not empty
if(empty($username) || empty($password))
{
die('Blank fields not allowed');
}
// Check if username length is in between 4 and 16
if(strlen($username) < 4 && strlen($username) > 16)
{
die('Username must be in between 4 and 16 characters');
}
// Check if username is alphanumeric
if(!ctype_alnum($username))
{
die('Username must be alphanumeric');
}
// Check password length
if(strlen($password) < 8)
{
die('Passwords should be at least 8 characters long');
}
// Try to login
$verified = $user->verify($username, $password);
if($verified)
{
header('Location: index.php');
die();
} else {
die('Invalid username/password');
}
}
?>
// HTML goes here
注销(logout.php)
<?php
require_once('classes/User.class.php');
$user = new User();
if($user->isLoggedIn())
{
unset($_SESSION['isLoggedIn']);
}
header('Location: login.php');
die();
索引 (index.php)
<?php
require_once('classes/User.class.php');
if(!$user->isLoggedIn())
{
header('Location: login.php');
die();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Welcome</title>
</head>
<body>
<h1>Menu</h1>
<ul>
<li><a href="logout.php">Logout?</a></li>
</ul>
</body>
</html>
How, during a login, do I check against the hashed password column only, and only table's column, if the passwords match, having only 'blabla' as data?
你不能。密码存储旨在使许多操作无法进行。如果您想在不使用用户名或其他密钥的情况下找到密码的匹配项,则需要对每个密码调用 password_verify
直到找到匹配项。按照设计,这将非常缓慢。
由于密码不需要是唯一的,您可能有一个密码可以匹配多个条目。
我猜这是个坏主意,不是您想要的。
无法使用数据库查询搜索正确加盐和散列的密码。您必须通过 username/email/... 搜索哈希,然后您可以使用找到的哈希验证输入的密码。
1) 首先查询存储的散列
SELECT passwordhash FROM users WHERE email = ?
2) 使用找到的哈希值验证输入的密码
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
是盐使搜索无法进行,必须先从存储的散列中提取盐,然后才能验证密码。这样的查询必须读取每个散列,提取其盐分并进行散列。因为散列函数非常慢(故意),查询将需要太长的时间才能执行。