0

我通过遵循各种教程和在线阅读文章从头开始创建了一个登录/注册系统。该系统工作,但有一个我不明白的错误。当用户第一次尝试登录时,它返回一个找不到帐户的错误,但是如果用户再次尝试登录,那么它会继续登录。我测试了它是否存储了会话 cookie,结果是它没有(至少不是第一次)。下次用户尝试登录时,它会正确存储 cookie。

这是我登录脚本的第一部分,它检查输入的验证码是否正确,然后设置会话 cookie 并将用户重定向到检查用户是否存在的登录页面脚本。

<?php
 session_start();
 $mode = $_GET['mode'];
 if($mode == 'login')
 {
     require_once('recaptchalib.php');
    $privatekey = "---";
    $resp = recaptcha_check_answer ($privatekey,
                                 $_SERVER["REMOTE_ADDR"],
                                 $_POST["recaptcha_challenge_field"],
                                 $_POST["recaptcha_response_field"]);
    if (!$resp->is_valid) {
    // What happens when the CAPTCHA was entered incorrectly
        header('Location: http://cpalander.net/login.php?option=captcha');
    } else {
        $user = $_POST['username2'];
        $pass = $_POST['password3'];
        $_SESSION['pass3'] = $pass;
        $_SESSION['user3'] = $user;
        header('Location: http://cpalander.net/login.php?option=checkuser');
    }
    die();
 }
 else if($mode == 'sendticket')
 {
     require_once('recaptchalib.php');
    $privatekey = "---";
    $resp = recaptcha_check_answer ($privatekey,
                                 $_SERVER["REMOTE_ADDR"],
                                 $_POST["recaptcha_challenge_field"],
                                 $_POST["recaptcha_response_field"]);
    if (!$resp->is_valid) {
    // What happens when the CAPTCHA was entered incorrectly
        header('Location: http://cpalander.net/dashboard.php?option=sendticket&error=captcha');
    } else {
        $subject = $_POST['subject'];
        $message = $_POST['message'];
        header('Location: http://cpalander.net/dashboard.php?option=sendticket&subject=' . urlencode($subject) . '&message=' . urlencode($message));
    }
    die();
 }
 else
 {
    require_once('recaptchalib.php');
    $privatekey = "---";
    $resp = recaptcha_check_answer ($privatekey,
                                 $_SERVER["REMOTE_ADDR"],
                                 $_POST["recaptcha_challenge_field"],
                                 $_POST["recaptcha_response_field"]);
    if (!$resp->is_valid) {
    // What happens when the CAPTCHA was entered incorrectly
        header('Location: http://cpalander.net/register.php?option=captcha');
    } else {
        $_SESSION['user2'] = $_POST['username'];
        $_SESSION['pass2'] = $_POST['password'];
        $_SESSION['mail2'] = $_POST['email'];
        header('Location: http://cpalander.net/makeacc.php');
    }
    die();
 }
 ?>

这是检查用户是否存在并在出现错误时重定向用户的代码部分:

<?php session_start();

 $data = $_GET["option"];
 $user = $_SESSION['user3'];
 $pass = $_SESSION['pass3'];

 function generateRandomString($length = 10) 
                    {
                        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
                        $randomString = '';
                        for ($i = 0; $i < $length; $i++) {
                            $randomString .= $characters[rand(0, strlen($characters) - 1)];
                        }
                        return $randomString;
                    }
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>....</head>...

<body>...    

...<?php
                if ($data == 'checkuser')
                {
                        $user = $_SESSION['user3'];
                        $link = new mysqli('127.0.0.1', '*******', '*******', '*******');
                        if ($link->connect_errno) {
        die('Failed to connect to MySQL: (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
     }                  
                        $result = $link->query("SELECT * FROM users WHERE username='$user' AND active=1 AND banned=0");
                        $numrows = $result->num_rows;
                        if($numrows == 0)
                        {
                            $link->close();
                            session_destroy();
                            echo '<META HTTP-EQUIV="Refresh" Content="0; URL=login.php?option=notfound&user=' . $user . '">'; // This is the part that I used to check if the $user variable is set
                            exit;   
                        }
                        $row = $result->fetch_assoc();
                        $sid = $row['salt'];
                        $pass_h = hash('sha256', $sid . $pass);
                        $result = $link->query("SELECT * FROM users WHERE username='$user' AND password='$pass_h' AND active=1 AND banned=0");
                        $numrows = $result->num_rows;
                        if($numrows == 0)
                        {
                            $link->close();
                            session_destroy();
                            echo '<META HTTP-EQUIV="Refresh" Content="0; URL=login.php?option=notfound">';
                            exit;   
                        }
                        else
                        {
                            $link->close();
                            $_SESSION['user'] = $user;
                            echo '<META HTTP-EQUIV="Refresh" Content="0; URL=dashboard.php?option=home&user=' . $user . '">';
                            exit;
                        }
                        $link->close();
                }...</body>

谁能帮我解决这个问题?另外, session_start(); 位于登录页面代码的顶部。

4

3 回答 3

0

代码看起来好像应该可以工作。您在找不到用户时销毁会话,因此没有理由让它第二次而不是第一次工作......除非之前发生了一些事情,这依赖于一些会话数据,并且您没有发布.

唯一看起来有问题的是两个登录字段分别命名为 username2 和 password3,但我想如果这是一个问题,你应该每次都会出错 - 除非你生成“username2”或“username3”以响应不同的条件?

除此之外,我还有一些建议:

1) 的

require_once('recaptchalib.php');

可以放在开头,因为无论如何您似乎都在使用它。

然后代替

    $user = $_POST['username2'];
    $pass = $_POST['password3'];
    $_SESSION['pass3'] = $pass;
    $_SESSION['user3'] = $user;

你可以放(但稍后会更多)

    $_SESSION['pass3'] = $_POST['password3'];
    $_SESSION['user3'] = $_POST['username2'];

而且你不需要三个单独的 die() - 实际上我认为你不需要它们。

在这一点上:

if ($data == 'checkuser')
    {
        $user = $_SESSION['user3'];

您没有检查是否实际设置了 $_SESSION['user3']。你可能会因此而出错。实际上,您之前已经分配了 $user (再次检查它是否已设置)。

这里的这一排真的很危险

$result = $link->query("SELECT * FROM users WHERE username='$user' AND active=1 AND banned=0");

因为如果我向您发送一个值为“bobby”或“=”的“username2”,您的查询将变为:

$link->query("SELECT * FROM users WHERE username='bobby' OR ''='' AND active=1 AND banned=0");

并且因为 ''='' 总是正确的,所以即使是不活动的和被禁止的,小 Bobby Tables 也可能已经登录。

然后,您将密码明文存储在会话中,我承认这几乎没有危险 - 但这仍然比没有危险更糟糕。为什么不存储哈希值呢?

$_SESSION['pass'] = hash('sha256', $_POST['password3']); // unsalted password
$_SESSION['user'] = hash('sha256', $_POST['username2']); // unsalted username

...

$link->query("SELECT * FROM users WHERE SHA2(username,256)='$user'
        AND SHA2(CONCAT(salt,'$pass'),256)=password AND active=1 AND banned=0");

现在,无论在 $_POST 中放置什么,它都会被研磨并消化成两个安全的哈希值。会话不再包含用户名或密码。密码仍然是盐渍的。如果用户不存在、被禁止、不活动或密码错误,则查询将不会产生任何结果,并且其成本将始终相同,从而击败了大多数定时攻击。

如果您保持用户名/密码清晰,那么您需要使用mysqli_escape()它来避免 SQL 注入攻击(或使用带有绑定参数的准备好的语句)。

于 2013-05-05T22:04:04.067 回答
0

session_start应该存在于所有需要访问该$_SESSION变量的文件中。这意味着,如果您正在访问不相关的文件,则需要添加session_id() or session_start();它们(session_id() or session_start();部分是为了确保Session already started如果在同一请求中对同一文件进行了要求/包含,则不会出现可怕的警告)

session_destroy()销毁会话,您可能只想这样做session_unset(),因为当再次调用 anewsession_destroy()时将重新创建会话。session_start()但是如果你真的想销毁会话并重新开始,一个跨浏览器的方法是调用两者session_unset()session_destroy()IE

此外,您应该注意,除了问题本身之外,您通过将原始$_POST数据传递给 URL 而不检查它,从而允许您的代码存在 XSS 漏洞。

于 2013-05-05T21:30:53.650 回答
0

查看这篇文章上的日期,我发现我迟到了,但我发现了这个,因为我遇到了一个非常相似的问题。请参阅我在PHP 代码中复制 nocache=1 功能的帖子。

这是一个可以缩小问题范围的实验:浏览离开您的页面,转到浏览器的 cookie 并删除该域的 cookie,浏览回登录页面,然后检查是否有 PHPSESSID cookie。我的猜测是不会有。然后尝试登录(你说失败)。然后再次检查 cookie - PHPSESSID 将在那里。然后下一次登录尝试有效。

现在试试这个:再次清除该域的 cookie,然后浏览到?nocache=1URL 末尾带有 a 的登录页面。这次可能会有一个 PHPSESSID cookie,这次登录将在第一次尝试时起作用。

在我的情况下,这?nocache=1件事有效,但当我在本地主机上本地运行相同的代码时甚至不需要它。

它看起来绝对像是服务器上的会话处理设置。

于 2015-09-02T05:05:43.750 回答