首页 文章

PHP登录安全理念

提问于
浏览
0

我有一个系统登录用户并验证他们在页面上登录的想法 .
我意识到那里有很多系统,但我主要是好奇,如果我的想法是好的 . 我一直认为是重要的做法(如密码加密等) . 我真的使用了应用程序安全性,希望得到一些反馈 .

当用户登录时,他们的名称和密码是根据数据库进行验证的,密码是使用SHA256和随机生成的盐加密的,整个字符串(盐和加密密码都是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;
}

如果登录有效,则会为其分配令牌 . 此令牌类似于密码加密,但存储加密的纪元以及另一个随机盐 . 令牌,登录时间,到期时间和用户名存储在DB中,用户名和令牌作为会话信息发送 .

这是创建令牌的代码:

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代码 . (在这里说了之后,我意识到只是POST'ing密码是一个很大的禁忌) .

$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>";
    }

这是在创建帐户时生成密码salt和hash的位 .

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;
}

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

3 回答

  • -1

    只是对salt / hash系统进行评论:在数据库中存储盐和密码一样会破坏盐析的目的 - 如果数据库受到损害,从安全角度来看盐变得无用,因为它在那里有助于猜测/打破安全 . salt的目的是通过向正在散列的值添加适当长度的秘密字符串来增加猜测散列字所需的时间 .

  • 1

    首先,像这样的哈希是不安全的 . sha256可以轻松破解,至少对于短密码 . 你必须使用一些hash stretching . 看看你是否可以找到一些PBKDF2实现或使用wikipedia 's suggestion for a 2653100 with sha256. Moreover I don' t获得你拥有更多会话变量的成果 . 我不明白validateuser()做什么,它仍然依赖于会话ID,或者我错过了什么 .

  • 1

    sivann类似,我无法看到额外的spanel_token的原因 . 似乎有效的做法是确保在令牌的到期时间之后会话不再有效 . 由于 WHERE 条件的token和user_id的值都存储在会话中,并且仅在登录期间设置,因此它们不会更改 . But session expiration can be implemented much easier.


    但除此之外更重要的是:您的代码容易受到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 注入到每个记录中而不是原始哈希列值 . 前导 0 是盐,剩下的字符串是空密码字符串的盐渍SHA-256哈希值,这将产生有效密码 .

    因此,我们最终输入密码字段的空字符串以及电子邮件字段的以下内容:

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

    这应该足以像任何用户一样“进行身份验证” .

相关问题