使用PDO预处理语句插入密码哈希值

3

在基本的mysql插入中,你可以设置一个密码变量'PASSWORD($password)',但这会破坏PDO语句。

当使用pdo::prepare和pdo::execute时,如何对密码进行哈希处理?

$sql= "INSERT INTO contractors (userid, password, name) VALUES ('$userid', '$pass1', '$name')";
$result = $dbh->prepare($sql);
$count = $result->execute();

Echo $count."<br>";

我是一个技术小白,简单的注册页面却花了我两天时间。希望能得到幼儿园级别的解答。

谢谢。


3
你应该使用bindParam()方法来传入这些参数。 - Joe Phillips
5个回答

13

注意

这个答案最初推荐了无盐哈希。但这在现今是愚蠢的,因此已经被重写以适应现代时代。请注意,在SO中旧内容中可能存在类似的现在不好的答案。

您正在使用PDO,因此应该使用占位符进行参数化查询:

$sql= "INSERT INTO contractors (userid, password, name) VALUES (?, ?, ?)";
$result = $dbh->prepare($sql);
$count = $result->execute(array($userid, $pass1, $name));

echo $count."<br>";

在现代时代,应该使用Blowfish/bcrypt而不是MD5或SHA1。自PHP 5.3以来,您可以使用crypt加上$2y$前缀。自PHP 5.5以来,您将能够使用password_hash。在此期间,您可以使用ircmaxell的password_compat库
以下是使用crypt和非常低的难度值的演示。请注意,我们还存储盐。虽然我在这个演示中硬编码了盐,但您应该为每个用户使用唯一的盐。
$salt = 'saltysaltsaltsalt'; 
$password_hash = crypt($pass1, '$2a$07$' . $salt);
$sql= "INSERT INTO contractors (userid, password, salt, name) VALUES (?, ?, ?, ?)";
$result = $dbh->prepare($sql);
$count = $result->execute(array($userid, $password_hash, $salt, $name));

echo $count."<br>";

验证密码就像使用相同参数重构哈希一样简单。

$sth = $dbh->prepare('SELECT password, salt FROM contractors WHERE userid = ?');
$sth->execute(array($userid));
list($existing_hash, $salt) = $sth->fetch(PDO::FETCH_NUM);
unset($sth);

$new_hash = crypt($pass1, '$2a$07$' . $salt);
if($new_hash === $existing_hash) {
    echo "Password matched.";
} else {
    echo "Password did not match.";
}

干得好,你比我先完成了。而且你说得比我好。 - Jed Smith
我认为你的execute()参数需要是一个数组。 - Joe Phillips
这更容易:https://dev59.com/h3nZa4cB1Zd3GeqPvdoa - partho

1
<?php
try {
  $dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $stmt = new PDOStatement();

  $hash = sha1($pass . $dataUniqueToEachUser);
  $stmt = $dbh->prepare("INSERT INTO Users(name, email, hash) VALUES (:name, :email, :hash)");
  $stmt->bindParam(':name', $_POST['username'], PDO::PARAM_STR);
  $stmt->bindParam(':email', $_POST['email'], PDO::PARAM_STR);
  $stmt->bindParam(':hash', $hash, PDO::PARAM_STR);
  $stmt->execute();

  if ($stmt->rowCount() == 0) {
   $valid = true;
  }
}
catch (PDOException $e) {
  echo "An error occurred: {$e}";
}
?>

0
如果您想使用MD5进行哈希,可以在构建SQL语句之前对密码执行以下操作:
$pass1 = md5($pass1);
$sql = "INSERT INTO contractors ( userid, password, name ) VALUES ( '$userid', '$pass1', '$name' )";
$result = $dbh->prepare($sql);
$count = $result->execute();

echo $count."<br>";

即使使用另一个哈希函数,这个想法仍然是相同的。在构建SQL语句之前对密码进行哈希处理。

正如FiarrVoteyDisciple在下面的评论中指出的那样,选择SHA哈希,因为它更安全。

sha1()


2
基于上述原因,我建议使用SHA(因为它不容易被破解),并使用盐。 - Fiarr
3
值得明确指出的是:这里有意不使用MySQL的PASSWORD()函数。PASSWORD()仅供MySQL自身使用,而不适用于开发人员在应用程序中创建密码。 md5()sha1()的工作原理基本相同,但更为安全。您可以从PHP或MySQL中调用任一函数。 - VoteyDisciple
1
这是一个便宜的在线课程,正在使用PASSWORD()。感谢您的提示和帮助。我会学习sha1()。感谢Alan和Fiarr。非常感激。 - Tom
此外,PHP还支持SHA-256,参见hash()函数。 - Bill Karwin
1
-1- 正如Joe Phillips所提到的,他应该使用占位符。 - Chris
显示剩余3条评论

0

如果您创建自己的哈希表可能更安全(也许更简单)。在我看来,最好的解决方案是通过连接表中的几个字段来创建哈希表,就像这样:

$pass1 = sha1($pass1.$name);

注意:如上所建议,md5不是最安全的解决方案,因为目前互联网上有很多数据库与md5哈希变量相关的关键字,这使得想要黑客入侵您的系统的人更容易实现。

1
仅仅将多个变长字段连接起来代替盐,可能会让你的应用程序容易受到一些不寻常的冒充攻击。 - user149341

0

使用变量替换会使您的应用程序面临 SQL 注入攻击的风险,除非您在粘贴的代码之前对 $userid 等进行转义。

最好使用 PDO 的替换功能(我是说,您已经调用了 .prepare):

$sql = "INSERT INTO contractors (userid, password, name) VALUES (?, PASSWORD(?), ?)";
$query = $dbh->prepare($sql);
$dbh->execute(array($userid, $pass1, $name));

我不确定像那样内联PASSWORD()是否有效,请纠正我如果我错了。如果你想采取Alan的方法,不依赖MySQL来哈希你的密码(明智的选择),你也可以这样做:

$sql = "INSERT INTO contractors (userid, password, name) VALUES (?, ?, ?)";
$query = $dbh->prepare($sql);
$dbh->execute(array($userid, sha1($hashed), $name));

考虑同时使用盐:

$hashed = sha1("SaltedPassword" . $pass1);

更好的做法是使用bindParam(),我认为。 - Joe Phillips
在我看来,bindParam()很愚蠢。只有PHP这样做,而且它使代码不够优雅。 - Jed Smith
1
如果你计划永远不更改代码,那么这是愚蠢的。在我的看法中,依靠索引来匹配参数所需的位置从来都不是一个聪明的想法。 - Joe Phillips

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接