【问题标题】:Database "validation" PHP数据库“验证”PHP
【发布时间】:2009-12-11 02:13:03
【问题描述】:

我正在尝试使用数据库中的密码验证用户输入的密码。我发现它可以很好地检查用户名(如果用户名不存在,则会显示错误),但是当它尝试使用 mysql 密码验证密码时,它永远不会工作。工作“示例”位于http://scapersclearing.com/fansite/login.php;而这就是PHP(注意base.php包含数据库信息和header.php、navigation.php和footer.php等所有前端)。一旦可行,我计划添加 html 实体并防止 SQL 注入。用户名:Test - 密码:password89 (md5 c1c2434f064da663997b1a2a233bf9f6)

<?php 
include("base.php"); //Include MySQL connection

$username = $_POST['username']; //Connect form username with strings
$password = $_POST['password']; //Connect form password with strings

$salt = "xia8u28jd0ajgfa"; //Define the salt string
$salt2 = "oqipoaks42duaiu"; //Define the second salt string
$password = md5($salt.$password.$salt2); //Encrypt the password

$result = mysql_query("SELECT * FROM members WHERE username = '".$username."'"); //Open the members table
while($row = mysql_fetch_array( $result )) { //Convert the members table into an array

if ( $username != $row['username'] ) { //If user entered username doesn't equal the database username
    include("header.php"); //Print the message
    include("navigation.php");
    echo "Invalid username or password!";
    include("footer.php");
}
else {

if ( $row['password'] == $password ) { //Validate username and password
    setcookie('c_username', $username, time()+6000);  //Set the username cookie
    setcookie('c_password', $password, time()+6000);  //Set the cookie
    header("Location:index.php"); //Redirect to home page
} else {
    include("header.php"); //Print the message
    include("navigation.php");
    echo "<div class=\"content\"><p>Invalid username or password!<p></div>";
    include("footer.php");
} } }
?>

【问题讨论】:

  • 让它回显数据库中的哈希值,以及脚本创建的哈希值是否匹配。
  • 使用SELECT * FROM MEMBERS WHERE username = ? and password = ? - 如果返回一行,则密码正确,否则不正确。您的数据库没有验证密码 - 在您的示例中,逻辑完全是 PHP。
  • 另外,当您将用户名直接传递到查询中时,您可能会遇到巨大的 SQL 注入漏洞。请改用 PDO。
  • @benjy:他确实在介绍中声明,一旦他的示例有效,他将在稍后添加...
  • “安全性不是附加功能” - Andrew S. Tanenbaum

标签: php database validation forms


【解决方案1】:

这是我的cmets:

<?php 
include("base.php"); //Include MySQL connection

如果'username''password' 索引不存在,则以下操作将失败。先用array_key_exists()测试$_POST数组。

$username = $_POST['username']; 
$password = $_POST['password'];

一种盐就足够了,将一半添加到开头,另一半添加到结尾,您将一无所获。

另外:您的代码 cmets 完全没用。不要费心用做出如此明显和无信息陈述的 cmets 来弄乱你的代码。

$salt = "xia8u28jd0ajgfa"; //Define the salt string
$salt2 = "oqipoaks42duaiu"; //Define the second salt string

但是为每个用户存储一个不同随机盐会更安全。您可以将其存储在数据库中的members 表中。使用盐的目的是击败字典攻击。如果攻击者知道您对所有成员帐户使用单个盐,那么他可以使用您的盐从字典单词中创建一组预先计算的哈希值。

MD5() 不是一个非常强大的哈希函数。已经发布了一些破坏 MD5 的方法。更好的是 SHA1,但更好的是 SHA-256。 PHP 中的 hash() 函数支持 SHA-256,但 MySQL 中尚不支持它作为本机函数。

$password = md5($salt.$password.$salt2);

您的查询中存在广泛的 SQL 注入漏洞。我强烈警告不要“稍后修复安全漏洞”,因为对于某些查询很容易忘记这样做。只需预先编写安全代码,您就不会错过任何内容。使用 SQL 查询参数很容易,因此不要在查询中插入不安全的变量。

现在真的没有理由使用过时的 ext/mysql API,而且这是不好的做法,因为它不支持查询参数等功能。使用 ext/mysqli 或其他 PDO。

$result = mysql_query("SELECT * FROM members WHERE username = '".$username."'");
while($row = mysql_fetch_array( $result )) {

如果您只是查询匹配的行,为什么 $username 会与 $row['username'] 不同?您应该测试一个空的结果集,如果用户输入一个不存在的用户名,就会发生这种情况——在这种情况下,您的 while() 循环将迭代零次,并且以下代码将永远不会运行。

if ( $username != $row['username'] ) {
    include("header.php"); //Print the message
    include("navigation.php");
    echo "Invalid username or password!";
    include("footer.php");
}
else {
. . .

在验证密码时,我更喜欢运行如下查询:

SELECT (password = SHA1(CONCAT(?, salt))) AS password_matches
FROM members WHERE username = ?

如果查询返回一个空的结果集,则给出的用户名是错误的。如果它返回一行但password_matches 为零,则给出的密码错误。当然,您不想向用户透露 哪个 是错误的,但出于自己的目的,您可能想知道。例如,应锁定收到多次失败登录尝试的给定帐户。

如果要使用 SHA-256,则必须获取用户的 salt,然后在 PHP 代码中使用 hash()

$stmt = $pdo->prepare("SELECT salt FROM members WHERE username = ?");
$stmt->execute( array($username) );
$rows = $stmt->fetchAll();
if (count($rows) > 0) {
  $password_hash = hash('sha256', $password . $rows[0]['salt']);
  $stmt = $pdo->prepare("SELECT password=? AS password_matches 
                         FROM members WHERE username = ?");
  $stmt->execute( array($password_hash, $username) );
  $rows = $stmt->fetchAll();
  if ($rows[0]['password_matches'] > 0) {
    // username and password are correct
  } else {
    // password was wrong
  }
} else {
  // username was not found
}

【讨论】:

    【解决方案2】:

    几点意见/建议:

    • 你应该在你的数据库中检查一个有效的用户名/密码组合,并且在成功的情况下应该返回一行,有效用户名的while循环只会在用户名不唯一时导致错误(我想他们应该是,但这取决于您)并且找到的第一行与您的密码不匹配。

    您的表中没有任何重复的用户,是吗?这将导致您描述的问题:第一行返回 false,输出被发送到浏览器并且无法再发送标题,尽管后面的行可能是有效的。

    • 您的第一个 if 语句没有意义,您已经选择了 $username == $row['username'] 的所有行,因此您基本上是在测试 1!=1

    所以我认为你需要简化你的代码,如果之后仍然有错误,它们会更容易找到。

    【讨论】:

      【解决方案3】:

      PHP 有一个 crypt 函数非常适合这种事情。 www.php.net/crypt

      它会自动生成随机盐并将其与密码一起存储,使其非常易于使用。请看下面的代码。

      将此答案与其他有关准备好的陈述的答案结合起来,您会很成功。

      这就是你的做法(我剪掉了很多代码,并采取了捷径,这样你就可以得到必需品):

      <?php
      # to register:
      query('INSERT INTO users (user, pass) VALUES (?, ?)', array($user, crypt($pass)));
      
      # to verify:
      $urow = query('SELECT user, pass FROM users WHERE user = ?', array($user));
      if (rows() < 1) {
        die('No such user');
      } 
      else if (crypt($pass, $urow['pass']) != $urow['pass']) {
        die('Wrong password!');
      } 
      #once you're here, it passed, and so on...
      ?>
      

      【讨论】:

        【解决方案4】:

        首先确保您生成的哈希值与您在注册​​时插入数据库中的哈希值相同。

        那么,您应该对用户进行身份验证的方式是:

        $res = mysql_query("SELECT just,the,needed,info FROM `members` WHERE `username` = '". esc($user) ."' AND `password` = '". esc($pass) ."'");
        
        if ($hlp = mysql_fetch_assoc($res))
            echo 'ok';
        else
            echo 'invalid username or password';
        

        其中 esc() 是您的最爱。转义函数(mysql_escape_string 或 mysql_real_escape_string)。

        现在在数据库中检查密码的有效性,所以你永远不会得到真正的哈希值。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-11-21
          • 1970-01-01
          • 1970-01-01
          • 2011-02-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-09-24
          相关资源
          最近更新 更多