我正在完成申请。我在脚本中进行了大量验证,但我想通过将密码发送到数据库时对密码进行散列来加强密码,以免密码不能以纯文本形式输入。我尝试了几种方法,但到目前为止还没有发现任何运气。
这是代码(托管在GitHub的Gist程序上):
https://gist.github.com/anonymous/e5c4f6ab9443d9ce887266b79bc36a1a
如您在脚本的密码验证部分中所看到的,我拥有该密码,如果密码通过所有其他验证,则将其哈希。但是当我检查数据库时它似乎不起作用。那是问题#1。最重要的是,我找不到任何能解散密码的方法,也找不到如何在上面链接中包含的login.php文件中实现“解散”技术的方法。感谢您提供的任何帮助或技巧。非常感谢!
最佳答案
有关(加密)哈希的一般信息
散列通常与加密混淆,但是它们是不同的,并且具有不同的用例:
Encryption用于编码某种东西,以便可以再次解密(使用右键),使其成为双向过程。
另一方面,(Cryptographic) hashing是一种单向过程,用于编码明显不是可解密的内容。此外,哈希函数是确定性的,这意味着给定相同的消息,它将始终产生相同的摘要。1
因此,这就是为什么使用加密哈希对密码进行编码的原因:
出于显而易见的安全原因,您不希望任何人都能够破解哈希密码(不要让那些能够破坏您的数据库的对手,甚至对您(网络管理员)也不会,因为那样会使您破坏用户的帐户(如果在其他地方使用相同的密码),但是您确实希望能够验证用户输入的密码与他们注册时使用的密码匹配。
如前所述,由于哈希函数是确定性的,因此我们可以通过将在身份验证过程(例如登录)中生成的哈希值与在注册过程中生成的哈希值进行比较,来实现此目的。
“但是等等……”,我听到您想,“在您的评论中,您告诉我,我无法简单地比较它们”。
如果提供给裸哈希函数的消息是相同的,则可以,这就是哈希函数在密码哈希上下文中确定性方面的问题:它们产生相同的输出。
这意味着,如果Alice,Bob和Charlie使用相同的密码,则它们都将产生相同的摘要:
id | username | pwdhash
----+----------+---------
17 | Alice | 9Wdm...
48 | Bob | 9Wdm...
57 | Charlie | 9Wdm...
这带来了两个突出的安全风险:
如果您知道爱丽丝的密码,那么您也知道其他人的密码;
攻击者可以知道您使用的哈希函数,从理论上生成所谓的rainbow table 2并将其哈希与您的哈希(如果您的数据库已被破坏)进行比较,以推断出与Rainbow表中的密码匹配的实际密码。
因此,为减轻这些风险,在对消息(密码)进行哈希处理之前,必须在消息(密码)上添加所谓的盐(salt),这是强制性的做法。盐应具有足够的唯一性,以保证两个相同的密码在馈入相同的哈希函数时会产生完全不同的摘要。
伪示例:
// produces same digests:
hash( 'password' ) -> bhZPmushb...
hash( 'password' ) -> bhZPmushb...
// produces unique digests:
salt message
hash( 'SeNib6' . 'password' ) -> qDHkfTW9m...
hash( '9pBq7y' . 'password' ) -> XH26YVUnA...
默认情况下,这就是
password_hash()
在后台自动为您执行的操作。3然后将用过的盐添加到摘要中,以及有关使用的实际算法和成本的信息。4password_verify()
将如果提供的密码与先前的哈希密码匹配,则使用此附加信息和用过的盐再次生成相同的摘要。因此,必须存储
password_hash()
的完整输出,以便可以将其作为第二个参数输入到password_verify()
中。那么,这对您的情况意味着什么?
由于
password_hash()
自动添加盐,因此会为相同的密码生成两个不同的摘要,因此您不能简单地比较password_hash()
的输出。这也意味着您不能(轻松地;如果有某种方法可以使它工作,我不会感到惊讶),可以像您想要的那样在数据库级别使用password = :password
比较密码哈希值;您将不得不比较PHP中的哈希,这意味着您必须首先获取用户的记录,然后验证密码哈希:$query = "SELECT * FROM credentials WHERE username = :username";
$statement = $conn->prepare($query);
$statement->execute(array('username' => $_POST["userName"]));
// I believe PDOStatement::rowCount() doesn't work with MySQL for SELECT statements
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
$count = count($rows);
if($count > 1) { // this might be redundant in your setup
// this is not supposed to happen, usernames should be unique
// log or alert developer
}
else if($count == 1) {
$row = $rows[0];
if(password_verify($_POST["userPass"], $row['password')) {
// don't store $_POST["userName"] in $_SESSION["username"], but use the database value
$_SESSION["username"] = $row["username"];
header("Location: user-homepage.php");
// it's good practice to exit after a redirect, unless other code still needs to be executed
exit();
}
}
$message = '<label>Invalid Credentials</label>';
1)散列函数的输出通常称为摘要或简称为散列。
2)彩虹表是通过为目标哈希函数提供大量可能的密码而生成的数据库,即通过蛮力生成一长串摘要,以与受损的摘要集进行比较。这通常是相当耗时的操作。
3)我实际上不确定
password_hash()
是在密码前面添加盐还是将其附加到摘要以生成摘要。4)有关format of the output(在有关密码哈希的FAQ中有解释),algorithms(在
password_hash()
中解释)和cost(在crypt()
中解释)的更多信息,请参见文档。
关于php - PHP PDO如何在注册时对密码进行哈希处理,然后在登录时“取消哈希处理”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47647066/