登录重定向上的无限循环

时间:2012-06-04 22:31:40

标签: php redirect login infinite-loop

我有一个通常适用于我的登录脚本,但在检查存储用于登录的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]");
?>

的functions.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;
}

?>

的config.php

<?

//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");

?>

2 个答案:

答案 0 :(得分:6)

编辑 - 试试这个:

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

  1. 用户试图访问页面
  2. 你应该检查是否允许他们
  3. 如果他们被允许,只需让他们不间断地查看页面
  4. 如果他们不是(即没有登录) - 将他们重定向到您的登录页面
  5. 即使允许他们查看页面(导致循环问题),您的脚本仍会尝试重定向它们。

    这是一个微妙的区别,但却是一个重要的区别(特别是当你保护所有页面时)。

    我会尝试这个:

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

    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不需要soley保护你的页面,我假设你用你的实际login.php脚本,因此:

    $_SESSION['url']
    

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

    最后上面是未经测试的代码,但应该比你的工作更好,让我知道你是如何进行的。


    说实话,很难确定你的代码究竟出了什么问题,因为还有一些未知变量,例如配置和函数文件以及函数:

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

    我认为用户应该是&#39;用户&#39;同样,除非它只是为这个问题松散地输入你所有的变量,如$ _SESSION [user_name]你必须确保你正确添加撇号或你会得到所有地方的通知(未定义的变量,假设等等),不要太提及这会弄乱你的会话数据。

    也许如果我就您当前的代码提出一些建议,您可以尝试一些可以修复代码的事情。

    多个重定向/脚本

    我首先要重写你的check_login.php和redirect.php脚本 - 事实上你有两个独立的脚本(可以合并)总会在某个阶段给你带来问题,因为基本上你是重定向到一个重定向(当你大声说出来的时候,它并不合乎逻辑。因此,首先将脚本重写为“auth.php&#39;脚本。并且还简化了包含需要身份验证的页面的内容,例如:

    <?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 [&#39; user_name&#39;]和$ _SESSION [&#39;密码&#39;]它可能就像:

    // 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 [&#39; redirect&#39;],否则我认为应该是$ _SESSION [&#39; url&#39;]或者只是不要重定向他们。 $ _SESSION [&#39;重定向&#39;]在redirect.php脚本(如果会话存在时可能无法调用)之前不会被设置。

    最后的想法:

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

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

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

答案 1 :(得分:0)

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

对于已禁用Cookie(隐私浏览)的用户,由于 check_login.php 中的行,所有用户都会卡住。因为这些值永远不会设置,会落入 check_login.php - &gt;的循环中。 login.php - &gt;的 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我真的建议你使用一些readily available会话管理片段。

我看到的问题,快速看一下:

a)正如史蒂夫所说,你不能在cookie中保存用户名和密码    EVER。您只保存随机会话ID值。

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

b)您的 functions.php SQL injection约束。

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

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

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

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