【问题标题】:PHP/mysqli throwing true every time [closed]PHP/mysqli 每次都抛出 true [关闭]
【发布时间】:2013-12-14 12:21:10
【问题描述】:

我有一些代码有问题,请弄清楚为什么会出错

//Searches the database for the username
    $mysqli_username = mysqli_query($mysqli, "SELECT username FROM ajbs_users WHERE username=`".$username."`");
    // if name does not exist
    if ($mysqli_username == null)
    {
        echo "<p>Username was inccorect, or user does not exist</p>";
    }
    else //if username is correct
    {
        //finds and stores password of the user
        $mysqli_userPassword = mysqli_query($mysqli, "SELECT password FROM ajbs_users WHERE password=`".$password."`");
        if ($mysqli_userPassword != $password) //checks password matches from user
        {
            echo "<p>Password was inccorrect</p>";
        }
        else
        {
            //Login
            echo "<p>You have been logged in";
            // Re directs to other page after creating a session
        }
    }

脚本不断输出“密码不正确”语句。

【问题讨论】:

  • ,使用前请阅读功能说明。只需几次击键:php.net/mysqli_query
  • $mysqli_userPassword 包含 MySQL 资源,不是字符串。您正在将结果对象与字符串进行比较,并且比较总是会评估为 FALSE

标签: php mysqli


【解决方案1】:

你的代码有很多问题:

  • mysqli_query() 不会从数据库中返回行,它会返回一个 resource,您可以从中获取数据库中的行。否则,如果查询产生解析错误或执行错误,则返回 false。您需要检查这些错误情况。

    或者使用mysqli_report(MYSQLI_REPORT_STRICT) 配置mysqli 以在出错时抛出异常(如果你知道如何使用异常进行编程)。

  • 您正在使用值 $username 和 $password 周围的反引号。反引号用于分隔标识符,如表名和列名。对于字符串值和日期值,请使用单引号。默认情况下,双引号与单引号相同,用于字符串和日期。但是,如果您将 MySQL 设置为 ANSI 模式,这可能会发生变化,然后像反引号一样使用双引号作为标识符。见MySQL's different quote marks

  • 您将变量直接插入到 SQL 表达式中,除非您非常小心地将它们转义,否则这是不安全的。这就是 SQL 注入的发生方式。阅读What is SQL injection?How can I prevent SQL injection in PHP? 您应该使用准备好的语句并使用参数向查询中添加动态元素。

    请记住,如果您使用参数,报价问题就会消失。您不要在参数周围加上任何类型的引号。 使用参数比将变量插入字符串更容易也更安全。

还有你的逻辑或应用程序设计的一些问题:

  • 您检查了用户名是否存在。然后,如果确实如此,则检查是否存在密码。但是在您的代码中,密码不一定由相同的用户名使用。您的代码不会检查 same 用户是否拥有该密码,只检查 any 用户拥有该密码。

    这意味着如果我可以使用我知道的密码注册一个用户名,例如“xyzzy”,我就可以入侵您的网站。然后我可以使用密码“xyzzy”以任何其他用户名登录。

  • 告诉用户用户名或密码是否不正确并不是一个好的安全做法。他们应该只被告知登录失败。如果他们是黑客,知道他们走在正确的轨道上对他们很有用,也就是说,他们选择了一个有效的用户名,他们只需要猜测密码。您可能希望记录这些信息以进行自己的故障排除,但在向用户报告时要更加模糊。

  • 您不应该存储明文密码。存储密码的哈希值。还要为每个用户存储一个随机盐,并在哈希计算中使用它。见You're Probably Storing Passwords IncorrectlyWhat data type to use for hashed password field and what length?

  • 如果您在一个查询中比较用户名和密码,您可以使用一个查询而不是两个查询来进行此搜索。我在 WHERE 子句中搜索用户名,然后返回存储的密码哈希与用户输入哈希的布尔比较结果。

所以我会这样写,记住上面的所有要点。

/*
 * Search the database for the username, and report if the password matches
 */
$sql = "SELECT (password = SHA2(CONCAT(?, salt), 256)) AS password_matches
        FROM ajbs_users WHERE username = ?";
if (($stmt = $mysqli->prepare($sql)) === false) {
    trigger_error($mysqli->error, E_USER_ERROR);
}
$stmt->bind_param("ss", $password, $username);
if ($stmt->execute() === false) {
    trigger_error($stmt->error, E_USER_ERROR);
}

$stmt->bind_result($password_matches);
if ($stmt->fetch()) {
    if ($password_matches) {
        echo "<p>You have been logged in";
        // Re directs to other page after creating a session
    } else {
        error_log("Login attempted with user '$username', but password was incorrect.");
    }
} else {
    error_log("Login attempted with user '$username', but user does not exist.");
}

// Tell the user only what they need to know.
echo "<p>Login failed. Username or password was incorrect</p>";

【讨论】:

  • +1 好答案!是否有使用 mysqli 准备语句的快捷方法,仍然保持代码简短?您有什么建议的方法吗?
  • @kevin,您仍然需要将 prepare() 和 execute() 作为单独的步骤。我更喜欢使用 PDO,因为不需要 bind_param() all。我可以将参数作为数组参数传递给execute()。请参阅 PDOStatement::execute() 中的示例 #2。
  • @kevin 当然有:github.com/colshrapnel/safemysql 虽然模拟了数据占位符,但它和原生一样安全
  • 当预期不超过一行时,while 循环没有用处
  • 根据“重定向到其他页面”的评论,你的这个循环应该只迭代一次。任何无故膨胀代码的操作符都是有害的。
猜你喜欢
  • 1970-01-01
  • 2014-07-19
  • 2015-09-18
  • 1970-01-01
  • 2020-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-12
相关资源
最近更新 更多