如何检查用户名和密码是否与数据库值匹配
How to check username and password matches the database values
如果这个问题看起来很愚蠢,我真的很抱歉。但是我已经尝试了几天来检查数据库中的 username
和 password
是否与我在 html
页面中输入的内容相匹配...这是我的登录表单...
<form method="POST" action="Dashboard/Dashboard.php">
<div class="form-group md-form">
<!--<input type="email" class="form-control" id="email" value="" placeholder="Enter email address">-->
<i class="fa fa-user prefix grey-text"></i>
<input name="username" id="username" type="text" class="form-control" required>
<label for="defaultForm-email">Username</label>
</div>
<div class="form-group md-form">
<!--<input type="password" class="form-control" id="password" value="" placeholder="Enter password">-->
<i class="fa fa-lock prefix grey-text"></i>
<input name="password" id="password" type="password" class="form-control" required>
<label for="defaultForm-pass">Your password</label>
</div>
<div class="text-center">
<button type="reset" class="btn btn-amber btn-sm"><strong>Reset</strong></button>
<input type="submit" name="submit" id="submit" class="btn btn-green btn-sm" value="Sign in">
</div>
</form>
这是我在 Dashboard.php
中使用的代码 (php
)
<?php
$servername = "localhost";
$username = "root";
$password = "";
$databaseName = "test";
$conn = mysqli_connect($servername, $username, $password, $databaseName);
$un = $_POST['username'];
$pw = $_POST['password'];
print $pass . "_" . $email;
$query = mysqli_query($conn, "SELECT log_username,log_password FROM login WHERE log_username='$un' AND log_password='$pw'");
$result_can = mysqli_query($conn, $query);
while ($row = mysql_fetch_assoc($result_can)) {
$check_username = $row['username'];
$check_password = $row['password'];
}
if ($un == $check_username && $pw == $check_password) {
$message = "ok";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
} else {
$message = "No";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
}
?>
我真的尝试了数千次,但无法弄清楚我哪里出错了...有人可以帮助我吗?
我知道我的代码对 SQL 注入开放,但我不关心它,因为这是我需要向朋友展示的示例 所以忽略那部分。
这里有几个问题,在你的代码和思维过程中。让我们继续往下看:
$un = $_POST['username'];
$pw = $_POST['password'];
print $pass . "_" . $email;
那 print
行 应该 给你一个警告。变量 $pass
和 $email
不存在。您应该删除该行,除非您 尝试 要做的是打印 $un
和 $pw
。
$query = mysqli_query($conn, "SELECT log_username,log_password FROM login WHERE log_username='$un' AND log_password='$pw'");
无需 select 用户名和密码列。如果匹配,它们将始终与您已有的 $un
和 $pw
相同。您只是检查用户名和密码是否正确,所以 select 单列就足够了。最好是用户 ID,但只有用户名就足够了。
请记住——假设查询成功执行——$query
将包含一个 mysqli_result
对象。
$result_can = mysqli_query($conn, $query);
需要删除此行。您已经执行了您的查询并且 $query
是它的结果,您在这里所做的事情毫无意义,应该给您一个警告,甚至可能是一个致命错误。
while ($row = mysql_fetch_assoc($result_can)) {
$check_username = $row['username'];
$check_password = $row['password'];
}
if ($un == $check_username && $pw == $check_password) {
$message = "ok";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
} else {
$message = "No";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
}
您不能混合使用mysql_*
和mysqli_*
函数。在这里使用 mysql_fetch_assoc()
应该会给你一个致命错误。您应该使用 mysqli_fetch_assoc()
代替(在 $query
而不是 $result_can
上),但是:
由于您只对是否有任何结果感兴趣,这整个部分可以更改为:
if (mysqli_num_rows($query) > 0) {
$message = "ok";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
} else {
$message = "No";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
}
这会带来其他问题,因为在回显您的 <script>
标记后您不能使用 header()
重定向用户(您将收到 "headers already sent" 错误)。如果您想要 Javascript 警报,请同时使用 Javascript 执行重定向。还有,那个$message
变量比较没用,你还不如直接把message放到alert里面:
if (mysqli_num_rows($query) > 0) {
echo "<script type='text/javascript'>alert('ok'); window.location.href='Doctors.php';</script>";
} else {
echo "<script type='text/javascript'>alert('No'); window.location.href='Doctors.php';</script>";
}
一旦您解决了 所有 这些问题,您仍然需要考虑一些事情。
- 您应该永远不要在您的数据库中以明文形式存储密码。
- 您现在可能不关心 SQL 注入,但根据您当前的查询,我可以通过输入用户名
admin' AND 1 --
作为任何有效用户(例如 "admin")登录,或者如果我只想访问,我可以使用 any' OR 1 --
的用户名并作为 table 中的第一个用户登录。查看准备好的语句及其工作原理。
- 你根本没有错误处理。您应该添加检查以查看数据库连接是否成功打开、查询是否正确执行、表单是否已发布以及 username/password 字段是否已填写,并考虑您希望如何向用户显示有用的错误消息。
这里的主要教训应该是:当你在开发并且它不起作用时,请始终检查错误日志以查看它是否包含任何提示并打开 PHP 的错误报告功能以便你可以在浏览器中看到你做错了什么。
几个问题,上面的评论提到了一些。
混合使用 mysql_* 与 mysqli_* API
您使用 mysqli_query()
调用查询,但您尝试使用 mysql_fetch_assoc()
获取结果。您不能混合使用这些不同的 API。 mysql_*
函数不会使用您用 mysqli_connect()
打开的连接,反之亦然。选择一个 MySQL 扩展并坚持使用。
提示:根本不要使用 mysql_*
。它已被弃用,并已从 PHP 7.0+
中删除
使用条件查询 用户名和密码
只需搜索用户名,然后获取密码。如果您搜索 both,则搜索将 return 零行,除非使用了正确的密码。
你不想要那个。您希望避免将明文密码放在 SQL 查询中。只需搜索用户名,获取存储的密码,然后将您获取的内容与用户输入的密码进行比较。
未初始化的变量
如果您从查询中获取零行,则永远不会设置 $check_username
和 $check_password
。然后在 if 语句中比较这些变量。不是致命错误,而是错误的风格。
没有密码散列
您似乎在将用户输入(我假设是纯文本)直接与数据库中存储的内容进行比较。 You're Probably Storing Passwords Incorrectly.
相反,当您存储密码时,请先使用 password_hash()。
没有查询参数
我知道你说过你不关心你的 SQL 注入漏洞,但这就像是一名电工并且说你不关心你的配电盘上塞满了油腻的破布。请务必 post 在您的 LinkedIn 个人资料中表明您对安全的漠视,以便雇主知道应该避免谁。
推荐实施
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // enable exceptions
$conn = new mysqli($servername, $mysql_username, $mysql_password, $databaseName);
$log_username = $_POST['username'];
$log_password = $_POST['password'];
$sql = "SELECT log_username, log_password_hash FROM login WHERE log_username=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param('s', $log_username);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
if (password_verify($log_password, $row['log_password_hash'])) {
$message = "ok";
// header must be called before any other output
header("Location: Doctors.php");
exit();
}
}
$message = "No";
// header must be called before any other output
header("Location: Doctors.php");
Stack Overflow 适用于“专业和狂热的程序员”。恕我直言,您已经向我们展示了您问题中的代码,这些代码甚至都不配得上这两个名字。它非常不安全,如果您将其放在 public 互联网上,您的网站 将 被网络犯罪分子破解。
Whosebug 的人对糟糕的安全代码没有多少幽默感。你对像你这样的代码会有强烈的反应,因为 Equifax、Ashley Madison、Adobe 以及所有其他已被网络犯罪分子破解的地方。我们为什么跳到你身上?因为我们不喜欢网络罪犯,我们不想让他们的生活变得轻松。朋友不要让朋友做坏密码安全。朋友不会向朋友展示非常不安全的密码验证码。
你的代码有什么问题?您将密码存储为纯文本,并且容易受到 SQL 注入攻击。我将解决这些问题中的第一个。
幸运的是,php 拥有业界领先的出色密码安全设施。在这里阅读它们。 http://php.net/manual/en/faq.passwords.php 使用它们。你如何处理密码?
- 当用户在您的网站上注册并首次出示密码时,您会在您服务器上的代码 运行 中对其进行哈希处理,类似这样。
$usersPassword = $_POST['password']);
$hash = password_hash( $usersPassword , PASSWORD_DEFAULT );
// you then store the username and the hash in your dbms.
// the column holding the hash should be VARCHAR(255) for future-proofing
// NEVER! store the plain text (unhashed) password in your database
当用户尝试登录时,您在服务器上执行如下查询:
SELECT log_password FROM log_user WHERE log_username = TheUsernameGiven
然后您将检索到的密码放入名为 $hash
的变量中。
然后您再次在您的服务器上使用 php's password_verify()
function 来检查您的潜在用户刚刚给您的密码是否与您数据库中的密码匹配。
最后,在您的服务器上检查用户密码是否需要重新哈希,因为您之前使用的哈希方法已经过时。
$usersPassword = $_POST['password']);
$valid = password_verify ( $usersPassword, $hash );
if ( $valid ) {
if ( password_needs_rehash ( $hash, PASSWORD_DEFAULT ) ) {
$newHash = password_hash( $usersPassword, PASSWORD_DEFAULT );
/* UPDATE the user's row in `log_user` to store $newHash */
}
/* log the user in, have fun! */
}
else {
/* tell the would-be user the username/password combo is invalid */
}
这个序列是面向未来的,因为如果旧的散列方法太容易被 cybercreeps 破解,它可以在以后重新散列密码。许多用户帐户的生命周期比 php.
等软件包版本长得多
要使密码等凭据保密,您必须使用 https 而不是 http 来连接浏览器和服务器。否则,网络犯罪分子可以拦截从您的用户到您的服务器的流量并获取她的密码。装配支持 https 的服务器可能会让人头疼,但它是部署 Web 应用程序的关键部分。 (像 Heroku 这样的服务可以让您轻松地使用 https 测试您的应用程序。)
如果这个问题看起来很愚蠢,我真的很抱歉。但是我已经尝试了几天来检查数据库中的 username
和 password
是否与我在 html
页面中输入的内容相匹配...这是我的登录表单...
<form method="POST" action="Dashboard/Dashboard.php">
<div class="form-group md-form">
<!--<input type="email" class="form-control" id="email" value="" placeholder="Enter email address">-->
<i class="fa fa-user prefix grey-text"></i>
<input name="username" id="username" type="text" class="form-control" required>
<label for="defaultForm-email">Username</label>
</div>
<div class="form-group md-form">
<!--<input type="password" class="form-control" id="password" value="" placeholder="Enter password">-->
<i class="fa fa-lock prefix grey-text"></i>
<input name="password" id="password" type="password" class="form-control" required>
<label for="defaultForm-pass">Your password</label>
</div>
<div class="text-center">
<button type="reset" class="btn btn-amber btn-sm"><strong>Reset</strong></button>
<input type="submit" name="submit" id="submit" class="btn btn-green btn-sm" value="Sign in">
</div>
</form>
这是我在 Dashboard.php
php
)
<?php
$servername = "localhost";
$username = "root";
$password = "";
$databaseName = "test";
$conn = mysqli_connect($servername, $username, $password, $databaseName);
$un = $_POST['username'];
$pw = $_POST['password'];
print $pass . "_" . $email;
$query = mysqli_query($conn, "SELECT log_username,log_password FROM login WHERE log_username='$un' AND log_password='$pw'");
$result_can = mysqli_query($conn, $query);
while ($row = mysql_fetch_assoc($result_can)) {
$check_username = $row['username'];
$check_password = $row['password'];
}
if ($un == $check_username && $pw == $check_password) {
$message = "ok";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
} else {
$message = "No";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
}
?>
我真的尝试了数千次,但无法弄清楚我哪里出错了...有人可以帮助我吗?
我知道我的代码对 SQL 注入开放,但我不关心它,因为这是我需要向朋友展示的示例 所以忽略那部分。
这里有几个问题,在你的代码和思维过程中。让我们继续往下看:
$un = $_POST['username'];
$pw = $_POST['password'];
print $pass . "_" . $email;
那 print
行 应该 给你一个警告。变量 $pass
和 $email
不存在。您应该删除该行,除非您 尝试 要做的是打印 $un
和 $pw
。
$query = mysqli_query($conn, "SELECT log_username,log_password FROM login WHERE log_username='$un' AND log_password='$pw'");
无需 select 用户名和密码列。如果匹配,它们将始终与您已有的 $un
和 $pw
相同。您只是检查用户名和密码是否正确,所以 select 单列就足够了。最好是用户 ID,但只有用户名就足够了。
请记住——假设查询成功执行——$query
将包含一个 mysqli_result
对象。
$result_can = mysqli_query($conn, $query);
需要删除此行。您已经执行了您的查询并且 $query
是它的结果,您在这里所做的事情毫无意义,应该给您一个警告,甚至可能是一个致命错误。
while ($row = mysql_fetch_assoc($result_can)) {
$check_username = $row['username'];
$check_password = $row['password'];
}
if ($un == $check_username && $pw == $check_password) {
$message = "ok";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
} else {
$message = "No";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
}
您不能混合使用mysql_*
和mysqli_*
函数。在这里使用 mysql_fetch_assoc()
应该会给你一个致命错误。您应该使用 mysqli_fetch_assoc()
代替(在 $query
而不是 $result_can
上),但是:
由于您只对是否有任何结果感兴趣,这整个部分可以更改为:
if (mysqli_num_rows($query) > 0) {
$message = "ok";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
} else {
$message = "No";
echo "<script type='text/javascript'>alert('$message');</script>";
header("Location: Doctors.php");
}
这会带来其他问题,因为在回显您的 <script>
标记后您不能使用 header()
重定向用户(您将收到 "headers already sent" 错误)。如果您想要 Javascript 警报,请同时使用 Javascript 执行重定向。还有,那个$message
变量比较没用,你还不如直接把message放到alert里面:
if (mysqli_num_rows($query) > 0) {
echo "<script type='text/javascript'>alert('ok'); window.location.href='Doctors.php';</script>";
} else {
echo "<script type='text/javascript'>alert('No'); window.location.href='Doctors.php';</script>";
}
一旦您解决了 所有 这些问题,您仍然需要考虑一些事情。
- 您应该永远不要在您的数据库中以明文形式存储密码。
- 您现在可能不关心 SQL 注入,但根据您当前的查询,我可以通过输入用户名
admin' AND 1 --
作为任何有效用户(例如 "admin")登录,或者如果我只想访问,我可以使用any' OR 1 --
的用户名并作为 table 中的第一个用户登录。查看准备好的语句及其工作原理。 - 你根本没有错误处理。您应该添加检查以查看数据库连接是否成功打开、查询是否正确执行、表单是否已发布以及 username/password 字段是否已填写,并考虑您希望如何向用户显示有用的错误消息。
这里的主要教训应该是:当你在开发并且它不起作用时,请始终检查错误日志以查看它是否包含任何提示并打开 PHP 的错误报告功能以便你可以在浏览器中看到你做错了什么。
几个问题,上面的评论提到了一些。
混合使用 mysql_* 与 mysqli_* API
您使用 mysqli_query()
调用查询,但您尝试使用 mysql_fetch_assoc()
获取结果。您不能混合使用这些不同的 API。 mysql_*
函数不会使用您用 mysqli_connect()
打开的连接,反之亦然。选择一个 MySQL 扩展并坚持使用。
提示:根本不要使用 mysql_*
。它已被弃用,并已从 PHP 7.0+
使用条件查询 用户名和密码
只需搜索用户名,然后获取密码。如果您搜索 both,则搜索将 return 零行,除非使用了正确的密码。
你不想要那个。您希望避免将明文密码放在 SQL 查询中。只需搜索用户名,获取存储的密码,然后将您获取的内容与用户输入的密码进行比较。
未初始化的变量
如果您从查询中获取零行,则永远不会设置 $check_username
和 $check_password
。然后在 if 语句中比较这些变量。不是致命错误,而是错误的风格。
没有密码散列
您似乎在将用户输入(我假设是纯文本)直接与数据库中存储的内容进行比较。 You're Probably Storing Passwords Incorrectly.
相反,当您存储密码时,请先使用 password_hash()。
没有查询参数
我知道你说过你不关心你的 SQL 注入漏洞,但这就像是一名电工并且说你不关心你的配电盘上塞满了油腻的破布。请务必 post 在您的 LinkedIn 个人资料中表明您对安全的漠视,以便雇主知道应该避免谁。
推荐实施
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // enable exceptions
$conn = new mysqli($servername, $mysql_username, $mysql_password, $databaseName);
$log_username = $_POST['username'];
$log_password = $_POST['password'];
$sql = "SELECT log_username, log_password_hash FROM login WHERE log_username=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param('s', $log_username);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
if (password_verify($log_password, $row['log_password_hash'])) {
$message = "ok";
// header must be called before any other output
header("Location: Doctors.php");
exit();
}
}
$message = "No";
// header must be called before any other output
header("Location: Doctors.php");
Stack Overflow 适用于“专业和狂热的程序员”。恕我直言,您已经向我们展示了您问题中的代码,这些代码甚至都不配得上这两个名字。它非常不安全,如果您将其放在 public 互联网上,您的网站 将 被网络犯罪分子破解。
Whosebug 的人对糟糕的安全代码没有多少幽默感。你对像你这样的代码会有强烈的反应,因为 Equifax、Ashley Madison、Adobe 以及所有其他已被网络犯罪分子破解的地方。我们为什么跳到你身上?因为我们不喜欢网络罪犯,我们不想让他们的生活变得轻松。朋友不要让朋友做坏密码安全。朋友不会向朋友展示非常不安全的密码验证码。
你的代码有什么问题?您将密码存储为纯文本,并且容易受到 SQL 注入攻击。我将解决这些问题中的第一个。
幸运的是,php 拥有业界领先的出色密码安全设施。在这里阅读它们。 http://php.net/manual/en/faq.passwords.php 使用它们。你如何处理密码?
- 当用户在您的网站上注册并首次出示密码时,您会在您服务器上的代码 运行 中对其进行哈希处理,类似这样。
$usersPassword = $_POST['password']);
$hash = password_hash( $usersPassword , PASSWORD_DEFAULT );
// you then store the username and the hash in your dbms.
// the column holding the hash should be VARCHAR(255) for future-proofing
// NEVER! store the plain text (unhashed) password in your database
当用户尝试登录时,您在服务器上执行如下查询:
SELECT log_password FROM log_user WHERE log_username = TheUsernameGiven
然后您将检索到的密码放入名为
$hash
的变量中。然后您再次在您的服务器上使用 php's
password_verify()
function 来检查您的潜在用户刚刚给您的密码是否与您数据库中的密码匹配。最后,在您的服务器上检查用户密码是否需要重新哈希,因为您之前使用的哈希方法已经过时。
$usersPassword = $_POST['password']);
$valid = password_verify ( $usersPassword, $hash );
if ( $valid ) {
if ( password_needs_rehash ( $hash, PASSWORD_DEFAULT ) ) {
$newHash = password_hash( $usersPassword, PASSWORD_DEFAULT );
/* UPDATE the user's row in `log_user` to store $newHash */
}
/* log the user in, have fun! */
}
else {
/* tell the would-be user the username/password combo is invalid */
}
这个序列是面向未来的,因为如果旧的散列方法太容易被 cybercreeps 破解,它可以在以后重新散列密码。许多用户帐户的生命周期比 php.
等软件包版本长得多要使密码等凭据保密,您必须使用 https 而不是 http 来连接浏览器和服务器。否则,网络犯罪分子可以拦截从您的用户到您的服务器的流量并获取她的密码。装配支持 https 的服务器可能会让人头疼,但它是部署 Web 应用程序的关键部分。 (像 Heroku 这样的服务可以让您轻松地使用 https 测试您的应用程序。)