1

我有一个通常对我有用的登录脚本,但在检查存储用于登录的 cookie 后,偶尔会在重定向时进入无限循环。浏览器将报告类似以下内容:“Firefox 检测到服务器正在以永远不会完成的方式重定向对该地址的请求。” 其他人也报告了这个问题。以下是登录过程的关键要素。我想知道是否有人可以看到这个过程/脚本有什么问题。

谢谢,

缺口

首先在每个受保护页面的顶部

<?php
session_start();

$_SESSION['url'] = $_SERVER['REQUEST_URI'];

require('login/config.php');
require('login/functions.php'); 

if (allow_access(Users) != "yes")
{ 
include ('login/check_login.php'); 
exit;
}
?>

然后在check_login.php

<?
session_start();

//check to see if the user already has an open session
if (($_SESSION[user_name] != "") && ($_SESSION[password] != ""))
{

    header("Location:$_SESSION[redirect]");
    exit;
}

$lr_user = $_COOKIE['lr_user'];
$lr_pass = $_COOKIE['lr_pass'];

//check to see if cookies have been set previously
if(($lr_user != "") && ($lr_pass != ""))
{
    header("Location:/login/redirect.php");
    exit;
}

//if neither is true, redirect to login
    header("Location:/login/login.php");

?>

然后,在redirect.php 中:

<?
session_start();

//require the functions file
require ("config.php");
require ("functions.php");

$lr_user = $_COOKIE['lr_user'];
$lr_pass = $_COOKIE['lr_pass'];

//check to see if cookies are already set, remember me
if ((!$lr_user) || (!$lr_pass))
{

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

}else{

$username = $lr_user;
$password = $lr_pass;

}

//sets cookies to remember this computer if the user asks to
if ($_POST[remember] == "Yes")
{
setcookie("lr_user", $username, $duration, "/", $domain);
setcookie("lr_pass", $password, $duration, "/", $domain);
}

//sets session variables
sess_vars($base_dir, $server, $dbusername, $dbpassword, $db_name, $table_name, $username, $password);

if(isset($_SESSION['url']))
  $_SESSION[redirect] = $_SESSION['url']; // holds url for last page visited.
else 
   $_SESSION[redirect] = "/index.php"; // default page for

//redirects the user    
header("Location:$_SESSION[redirect]");
?>

函数.php

<?php

//function to get the date
function last_login()
{
    $date = gmdate("Y-m-d");
    return $date;
}

//function that sets the session variable
function sess_vars($base_dir, $server, $dbusername, $dbpassword, $db_name, $table_name, $username, $password)
{

    //make connection to dbase
    $connection = @mysql_connect($server, $dbusername, $dbpassword)
                or die(mysql_error());

    $db = @mysql_select_db($db_name,$connection)
                or die(mysql_error());

    $sql = "SELECT * FROM $table_name WHERE username = '$username' and password = password('$password')";

    $result = @mysql_query($sql, $connection) or die(mysql_error());


    //get the number of rows in the result set
    $num = mysql_num_rows($result);

    //set session variables if there is a match
    if ($num != 0) 
    {
        while ($sql = mysql_fetch_object($result)) 
        {
            $_SESSION[first_name]   = $sql -> firstname;
            $_SESSION[last_name]    = $sql -> lastname; 
            $_SESSION[user_name]    = $sql -> username;       
            $_SESSION[password]     = $sql -> password;
            $_SESSION[group1]       = $sql -> group1;
            $_SESSION[group2]       = $sql -> group2;
            $_SESSION[group3]       = $sql -> group3;
            $_SESSION[pchange]      = $sql -> pchange;  
            $_SESSION[email]        = $sql -> email;
            $_SESSION[redirect]     = $sql -> redirect;
            $_SESSION[verified]     = $sql -> verified;
            $_SESSION[last_login]   = $sql -> last_login;
        }
    }else{
        $_SESSION[redirect] = "$base_dir/errorlogin.php";
    }
}


//functions that will determine if access is allowed
function allow_access($group)
{
    if ($_SESSION[group1] == "$group" || $_SESSION[group2] == "$group" || $_SESSION[group3] == "$group" ||
        $_SESSION[group1] == "Administrators" || $_SESSION[group2] == "Administrators" || $_SESSION[group3] == "Administrators" ||
        $_SESSION[user_name] == "$group")
        {
            $allowed = "yes";
        }else{
            $allowed = "no";
        }
    return $allowed;
}

//function to check the length of the requested password
function password_check($min_pass, $max_pass, $pass)
{

    $valid = "yes";
    if ($min_pass > strlen($pass) || $max_pass < strlen($pass))
    {
        $valid = "no";
    }

    return $valid;
}

?>

配置文件

<?

//set up the names of the database and table
$db_name ="";
$table_name ="authorize";

//connect to the server and select the database
$server = "localhost";
$dbusername = "";
$dbpassword = "*";

//domain information
$domain = "";

//Change to "0" to turn off the login log
$log_login = "1";

//base_dir is the location of the files, ie http://www.yourdomain/login
$base_dir = "";

//length of time the cookie is good for - 7 is the days and 24 is the hours
//if you would like the time to be short, say 1 hour, change to 60*60*1
$duration = time()+60*60*24*365*10;

//the site administrator\'s email address
$adminemail = "";

//sets the time to EST
$zone=3600*00;

//do you want the verify the new user through email if the user registers themselves?
//yes = "0" :  no = "1"
$verify = "0";

//default redirect, this is the URL that all self-registered users will be redirected to
$default_url = "";

//minimum and maximum password lengths
$min_pass = 8;
$max_pass = 15;


$num_groups = 0+2;
$group_array = array("Users","Administrators");

?>
4

2 回答 2

6

编辑 - 试试这个:

我认为问题在于我假设您正在尝试保护还包括 index.php 的“所有”页面。但是,您已将 index.php 作为页面包含在 $_SESSION[redirect] 变量中。你应该按照以下思路思考:

  1. 用户尝试访问页面
  2. 你应该检查他们是否被允许
  3. 如果允许,就让他们不间断地查看页面
  4. 如果他们不是(即未登录) - 将他们重定向到您的登录页面

即使允许他们查看页面(这会导致循环问题),您的脚本仍在尝试重定向它们。

这是一个微妙的区别,但很重要(尤其是当您保护所有页面时)。

我会试试这个:

在受保护页面的顶部,将底部代码段更改为:

if (allow_access(Users) != "yes")
{ 
    include ('login/check_login.php'); 
    check_redirect();
}

在你的 check_login.php 试试这个:

<?
session_start();

function check_redirect()
{

    //check to see if the user already has an open session
    if (($_SESSION[user_name] != "") && ($_SESSION[password] != ""))
    {
       // just return if they are a valid user (no need to redirect them)
       return;
    }

    $lr_user = $_COOKIE['lr_user'];
    $lr_pass = $_COOKIE['lr_pass'];

    //check to see if cookies have been set previously
    if(($lr_user != "") && ($lr_pass != ""))
    {
       // just return if they are a valid user (no need to redirect them)
       return;
    }

    //if neither is true, redirect to login
    header("Location:/login/login.php");
    die();

}  
?>

您的 redirect.php 不需要单独保护您的页面,我假设您将其与实际的 login.php 脚本一起使用,因此:

$_SESSION['url']

将存储他们试图访问的页面,并且您的 redirect.php / login.php 脚本应该在成功登录后使用它来将它们重定向回那里。

最后,上面是未经测试的代码,但应该比你拥有的更好,让我知道你的进展情况。


老实说,很难确定您的代码到底出了什么问题,因为仍然有一些未知变量,例如配置和函数文件以及函数:

if (allow_access(Users) != "yes")

我假设用户应该是“用户”,同样,除非它只是为这个问题松散地输入了所有你拥有的变量,比如 $_SESSION[user_name] 你必须确保你正确地添加了撇号,否则你会在整个地方收到通知(未定义的变量,假设...等)更不用说这可能会弄乱您的会话数据。

也许如果我对您当前的代码提供了一些建议,您也许可以尝试一些可以修复您的代码的方法。

多个重定向/脚本

我会首先重写你的 check_login.php 和 redirect.php 脚本——事实上你有两个单独的脚本(可以组合)总是会在某个阶段给你带来问题,因为本质上你正在重定向到一个重定向(这不是从逻辑上讲,当你大声说出来时)。因此,首先将您的脚本重写为一个“auth.php”脚本。并且还简化了需要身份验证的页面的包含,例如:

<?php
    session_start();

    // use require_once for your login scripts, not 'include' as you want an error to 
    // occur to halt the page processing if the file is not found, include will just
    // give a warning but still continue with the page processing (which you're trying
    // to protect). Lastly '_once' so you don't get multiple inclusions of the same
    // script by accident (which is always import for login / redirect scripts). 
    require_once('login/auth.php');

    // just create one function which will proxy all the other functions / includes
    // you could exclude this function and just use the require_once file for direct 
    // access - but I would say including a function will make it easier to understand.
    check_login();
?>

现在 auth.php 文件:

<?php
    session_start();

    // if these are required use the _once. I would guess some of these 'functions'
    // may be able to be included within this 'auth.php' file directly?
    require_once('login/config.php');
    require_once('login/functions.php'); 

    // set your variables here
    $_SESSION['url'] = $_SERVER['REQUEST_URI'];

    // the main check login function
    function check_login() 
    {

        // Check if your user is logged in / needs to be logged in
        // perhaps something from your allow_access() function?

        // Do all the checks for session / cookie you should resolve the checks to
        // a simple bool variable (i.e. if the user is valid or not)
        $userIsValid = true || false; // get this from above code

        // Then use the redirect function and pass in the $userIsValid variable
        // which will tell the redirect() function where to redirect to.
        redirect($userIsValid);

    }

    // use a separate function for the redirect to keep it cleaner 
    // not too sure on all the Url's you have floating around in your code as I
    // would think you either want to let them to proceed to the page they were
    // trying to view (if validated) or you want them to login?      
    function redirect($validUser = false)
    {
        // if the user is valid, just return as you don't have to redirect them
        if ( $validUser ) {
            return true;
        }
        // otherwise just redirect them to the login page
        header("Location:/login/login.php");
        die();
    }

?>

安全

您不需要(也不应该!)将实际密码存储在会话中,我当然也建议不要使用 cookie。但是,如果您必须将密码/用户名存储在 cookie 中,至少您必须使用 md5() 和盐等对其进行加密。所以简而言之,而不是检查 $_SESSION['user_name'] 和 $_SESSION['密码'] 它可能只是这样的:

// if the user has no valid session do something:
if ( !isset($_SESSION['id']) ) {


}

关注点分离

我不确定你为什么有:

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

在您的 redirect.php 文件中,您是否也在用户登录时使用此脚本?如果您是,我认为这不是一个好主意(这可能是问题所在)。您应该有一个单独的脚本来处理所有实际的登录功能(包括登录后的重定向)。以上内容应该只关注a)检查用户是否有效/登录b)如果不是则重定向它们 - 本质上保护您的网页。

您的代码:

//check to see if the user already has an open session
if (($_SESSION[user_name] != "") && ($_SESSION[password] != ""))
{

    header("Location:$_SESSION[redirect]");
    exit;
}

我不确定我是否在上下文中得到了这一点,因为如果他们有一个有效的会话,你基本上会重定向他们?从头开始,我并不真正了解整个 check_login.php 脚本,因为事情有点倒退(尤其是与您的 redirect.php 脚本结合使用时)。您正在再次检查相同的变量 ($lr_user) || (!$lr_pass) 在您的重定向脚本中,并引用甚至尚未在您的 check_login.php 脚本中设置的内容。

编辑:可以解决吗? 除非我看过上面的代码块引用了 $_SESSION['redirect'] 的东西,否则我认为应该是 $_SESSION['url'] 或者只是不重定向它们。$_SESSION['redirect'] 直到 redirect.php 脚本(如果会话存在,可能不会被调用)才被设置。

最后的想法:

抱歉,如果这并不能真正回答您希望的问题,但我真的认为这将是一个很好的机会来仔细查看您的脚本并清理/简化它们。理想情况下,您应该使用 OOP 方法来看待这个问题,即创建会话、重定向、登录类。但是,如果坚持使用普通函数(程序),请确保为每个脚本创建一个干净的分隔。简而言之:

  • 不要重复自己,例如为什么 $_SESSION['redirect'] 和 $_SESSION['url'] 这些是相同的值?
  • 不要重定向到重定向(一个脚本应该处理这个)
  • 分离您的顾虑 - 让登录脚本执行登录过程,并使用身份验证/acl 脚本保护您的实际页面(不要将两者结合起来)。

希望以上是有道理的,但如果没有,请告诉我。

于 2012-06-07T00:29:14.107 回答
0

如果您真的想使用此代码,我认为这是答案:

对于禁用 cookie(隐私浏览)的用户,您的所有用户都会因为check_login.php中的行而卡住。因为这些值永远不会被设置,并且会陷入check_login.php -> login.php -> check_login.php的循环:

$lr_user = $_COOKIE['lr_user']; 
$lr_pass = $_COOKIE['lr_pass']; 

//check to see if cookies have been set previously 
if(($lr_user != "") && ($lr_pass != "")) 
{     
header("Location:/login/redirect.php");     
exit; 
}

//if neither is true, redirect to login        
header("Location:/login/login.php"); 

Bur 我真的建议您使用一些现成的会话管理片段。

我快速浏览了一下发现的问题:

a) 正如史蒂夫所说,您永远不能将用户名和密码保存在 cookie 中。您只保存一个随机会话 id 值。

setcookie("lr_user", $username, $duration, "/", $domain); 
setcookie("lr_pass", $password, $duration, "/", $domain); 

b) 您的functions.php受到SQL 注入的影响。

$sql = "SELECT * FROM $table_name WHERE username = '$username' and 
password = password('$password')";

c) 用户输入永远不会被转义。

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

d) 您在整个代码中拆分了会话管理检查。

于 2012-06-07T09:56:54.917 回答