I cannot resolve "redirect loop" - php

This webpage has a redirect loop.
The above has been discussed and answered broadly here but, after following the recommendations found here I still cannot resolve problem with "header("Location:...)".
The code is executed when user clicks a "forgotten password" link in email. The link has random string attached to it like ".../?trs=randomstringstoredindatabase".
When code executes:
if string is valid I get the following results:
Case with code lines "header...;: & "exit;" uncommented:
URL in browser: http://localhost/pl_00_00/pl_process_forgot_password_back.php/pl_reset_password.php/index.php
Chrome displays: This webpage has a redirect loop
Case with code lines "header...;: & "exit;" commented out:
URL in browser: http://localhost/pl_00_00/pl_process_forgot_password_back.php/?trs=015df6fcf5bdcd4d9a339d5ca79d27a7 - correct
Chrome displays echo: "Redirect to: pl_reset_password.php/?memid=11" - as expected
otherwise (string not valid):
Case with code lines "header...;: & "exit;" uncommented:
URL in browser: http://localhost/pl_00_00/pl_process_forgot_password_back.php/index.php
Chrome displays: This webpage has a redirect loop
Case with code lines "header...;: & "exit;" commented out:
URL in browser: http://localhost/pl_00_00/pl_process_forgot_password_back.php/?trs=015df6fcf5bdcd4d9a339d5ca79d27a
Chrome displays echo: "Redirect to: index.php" - as expected
So, when not using "header()" function the logic seems correct. As soon as I uncomment "header()" & "exit" and comment out echos and var_dumps, the problem appears and redirections are not happening. Here is the code I use (PASSWORD_RESET_PAGE & HOME_PAGE are constants defined elsewhere and both do not have function "header ("Location...")" that could cause redirect loop in them):
<?php
# pl_process_forgot_password_back.php
require_once ("lib/required.php");
# $_GET the string from url check and if matches, active and not expired display password change form
$trs_from_email = $_GET['trs'];
# retrieve user data
$query = "SELECT * FROM tbl_member WHERE temporary_random_string = '" . $trs_from_email . "'";
$data_retrieved = sql_get_results_array($query); # retrieve data
if (db_affected_rows() == 1) { # string matched - 1 row selected
if ($data_retrieved[0]['random_string_active']){ # string active
if ($data_retrieved[0]['random_string_expiry'] > time()){ # string not expired
# all ok - display reset password form
header ( "Location: " . PASSWORD_RESET_PAGE . '/?memid=' . $data_retrieved['id'] ); # send them to page with reset-password form
exit;
echo '<br>' . '# all ok - display reset password form';
echo '<br>' . 'Redirect to: ' . PASSWORD_RESET_PAGE . '/?memid=' . $data_retrieved[0]['id'];
var_dump($_SESSION);
var_dump($data_retrieved);
die;
} else { # string expired
$_SESSION['popup_msg_id'] = 17; # this is to popup request expiry message
} # /if string not expired
} else { # string inactive
$_SESSION['popup_msg_id'] = 18; # request already processed message
} # /if string active
} else { # string not matched
$_SESSION['popup_msg_id'] = 19; # string not matched message
} # /if valid string found
header ( "Location: " . HOME_PAGE ); # send them to homepage and display popup error
exit;
echo '<br>' . '# some error';
echo '<br>' . 'Redirect to: ' . HOME_PAGE;
var_dump($_SESSION);
var_dump($data_retrieved);
die;
?>
Can anybody see why the above code is causing the "redirect loop"?

Keep in mind that die and exit are equivalent, so pick one.
The script will not continue to execute after die or exit is hit in the script. Because of this you are cutting off your echo statements and var_dumps, which should be before header if you want them to be seen, but a redirect can be pretty quick which means they may not be seen at all.
a redirect loop can happen if you are redirecting to the same page that you are currently on or if you keep redirecting the user on subsequent pages. If you are sure this is not the case then you need to remove all the cookies and browser cache associated with your site which should fix the problem.
The following sample and place your code in the correct order.
<?php
# pl_process_forgot_password_back.php
require_once ("lib/required.php");
# $_GET the string from url check and if matches, active and not expired display password change form
$trs_from_email = $_GET['trs'];
# retrieve user data
$query = "SELECT * FROM tbl_member WHERE temporary_random_string = '" . $trs_from_email . "'";
$data_retrieved = sql_get_results_array($query); # retrieve data
if (db_affected_rows() == 1) { # string matched - 1 row selected
if ($data_retrieved[0]['random_string_active']){ # string active
if ($data_retrieved[0]['random_string_expiry'] > time()){ # string not expired
# all ok - display reset password form
echo '<br>' . '# all ok - display reset password form';
echo '<br>' . 'Redirect to: ' . PASSWORD_RESET_PAGE . '/?memid=' . $data_retrieved[0]['id'];
var_dump($_SESSION);
var_dump($data_retrieved);
header ( "Location: " . PASSWORD_RESET_PAGE . '/?memid=' . $data_retrieved['id'] ); # send them to page with reset-password form
exit;
} else { # string expired
$_SESSION['popup_msg_id'] = 17; # this is to popup request expiry message
} # /if string not expired
} else { # string inactive
$_SESSION['popup_msg_id'] = 18; # request already processed message
} # /if string active
} else { # string not matched
$_SESSION['popup_msg_id'] = 19; # string not matched message
} # /if valid string found
echo '<br>' . '# some error';
echo '<br>' . 'Redirect to: ' . HOME_PAGE;
var_dump($_SESSION);
var_dump($data_retrieved);
header ( "Location: " . HOME_PAGE ); # send them to homepage and display popup error
exit;
?>

Related

How to fix automatic logout problems,when a new user login in php

How do i fix automatic logout problems,when a new user login into the system.i also used two machine to test my login and logout, but it keeps one user logged out when a new user login...please help
switch ($row["status"]) {
case 'Admin':
session_id('admin');
session_start();
$_SESSION['Ausername'] = $username;
$_SESSION['Auser_id'] = $user_id;
$_SESSION['Amycompany'] = $seleCompany;
if (isset($username)) {
setcookie('Administrator',$username,time()+60*60*7);
}
$_SESSION['success'] = " Welcome " . $username . " to PSL Lunch";
header('Location:persol/admin/index.php');
break;
case 'Member':
session_id('member');
session_start();
$_SESSION['username'] = $username;
$_SESSION['user_id'] = $user_id;
$_SESSION['mycompany'] = $seleCompany;
if (isset($username)) {
setcookie('Member',$username,time()+60*60*7);
}
$_SESSION['success'] = "Welcome " . $username . " to PSL Lunch";
header('Location:persol/member/index.php');
break;
default:
$_SESSION['warning'] = "Username or Password is incorrect";
header('Location:index.php');
}
my login php script for both the admin and member page
I would do a couple things here, the main one being I wouldn't mess with the session_id() except maybe to reset it after log in. There are other ways to indicate user type. You can store the usergroup in the session. Secondly, I would use the $_SESSION to save the activity time instead of cookies. Users can turn cookies off in their browser. Lastly, I would make some useful functions to help keep things less repetitive and more reusable:
/functions/myfunctions.php
# Create a login function
function logInUser($username,$seleCompany,$isAdmin = false)
{
# Save type here
$_SESSION['usergroup'] = ($isAdmin)? 1 : 2;
# Save different info here
if($isAdmin) {
# Save admin session stuff
$_SESSION['Ausername'] = $username;
$_SESSION['Auser_id'] = session_id();
$_SESSION['Amycompany'] = $seleCompany;
}
else {
# Create member session
$_SESSION['username'] = $username;
$_SESSION['user_id'] = $user_id;
$_SESSION['mycompany'] = $seleCompany;
}
# Just save the status, not a message. On the redirected page, check
# the status and write the message there. Don't store the message
# to the session
$_SESSION['success'] = true;
}
# Use session to store time, user can disable cookies
function expireTimeListener($exp=3600)
{
# Now time
$now = strtotime('now');
# Create a future session expire time
$future = $now+$exp;
# If the user has just arrived at the site, create time
if(!isset($_SESSION['ACTIVE_TIME']))
$_SESSION['ACTIVE_TIME'] = $future;
else {
# If the current time is more that the stored time
if($now > $_SESSION['ACTIVE_TIME']) {
# First check if the user is logged in
if(isset($_SESSION['usergroup'])) {
# Destroy the session and redirect
session_destroy();
header('Location: /logout.php');
exit;
}
}
# If the current time is less than the future time, extend it
$_SESSION['ACTIVE_TIME'] = $future;
}
}
/login.php
# Include the functions
include_once(__DIR__.'/functions/myfunctions.php');
# Put this at the very top of your page, before everything else
session_start();
/**
* Do whatever here that generates the $row array
*/
# Set status here (string to lower for use later)
$status = strtolower($row["status"]);
# Verify that the status is a valid type
if(!in_array($status,array('admin','member')))
# If not a valid type, set it to false for safety
$status = false;
# Determine type
switch($status) {
case('admin'):
# Log in admin
logInUser($username,$seleCompany,true);
break;
case('member'):
# Log in member
logInUser($username,$seleCompany);
break;
default:
# Just save the status, not a message. On the redirected page, check
# the status and write the message there. Don't store the message
# to the session
$_SESSION['warning'] = false;
}
# Create redirect path
$header = (!empty($_SESSION['warning']) && !empty($status))? "persol/{$status}/" : '';
# Redirect
header("Location: {$header}index.php");
# Stop execution
exit;
On your other pages that don't include the login, add the expire listener:
/all_other_pages.php
# Include the functions
include_once(__DIR__.'/functions/myfunctions.php');
# Add to very top
session_start();
# Add directly after your expiration function
expireTimeListener();
One note on numeric values for usergroup status a benefit to that is that you can use greater-than-equal-to to show admin status so for instance:
if(isset($_SESSION['usergroup'])) {
if($_SESSION['usergroup'] <= 2)
echo 'If you ever add a new usergroup type (like 3), every logged in user can see content who is Member (2) and Admin (1), but not greater (3 or higher).';
elseif($_SESSION['usergroup'] >= 2)
echo 'Only a member and lower status member can see content.';
elseif($_SESSION['usergroup'] < 2)
echo 'Only an admin can see content.';
}
else
echo 'You\'re not logged in.';

Why is my returnurl getting $ (dollar signs) in from on the forward slash hashed code (%2F)?

I have a php function to autolog in users via SSO to wordpress. It works fine but when users have a link in Excel or Word it redirects them to a login and then I need to return them via returnurl.
The link in Excel and Word spits out this for the returnurl
returnurl%3dhttps$%3A$%2F$%2Fsiteurl%2ecom$%2Fhome$%2Fcatalog$%2Ftransactional-connectivity$%2F
So the problem here is the $%2F should just be %2F for the forward slashes. Well you notice that everything is prefixed with a "$" but the forward slash can't deal with it because it doesn't compute on redirect and knocks user back to the home page instead of the page they clicked on.
The part of the PHP code I wrote that handles this is in this function -
function safe_SSO() {
global $wpdb;
$wpprefix = $wpdb->prefix;
$url = 'https://'.$_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
session_set_cookie_params(60 * 60 * 24 * 365);
session_start();
define('SAFE_APP_ID', get_option('AppID'));
define('SAFE_KEY', get_option('AppKey'));
$safeurl = 'https://safe.siteurl.com/login/sso/SSOService?app=' . SAFE_APP_ID;
define('HOME', urlencode($url));
define('SAFE_REDIRECT', $safeurl . "&returnurl=" . urlencode($url));
if(is_user_logged_in()) {
$current_user = wp_get_current_user();
}
else if(!is_user_logged_in()) {
// Start authentication and authorization
if (!isset($_SESSION['USER_ID']) && !isset($_POST['digest'])) {
// We've got no session and we've not returned from SAFE, so let's redirect to SAFE
header('Location: ' . SAFE_REDIRECT);
die();
}
if (!isset($_SESSION['USER_ID']) && isset($_POST['digest'])) {
// We've got no session but we've got a response from SAFE - let's check it
$safe_uid = $_POST['uid'];
$safe_name = $_POST['firstname'] . ' ' . $_POST['lastname'];
$safe_time = $_POST['time'];
$safe_email = $_POST['email'];
$safe_digest = $_POST['digest'];
$safe_returnurl = $_POST['returnurl'];

Remove query from URL before $_SERVER['HTTP_REFERER']

On success or fail of a form submission I am using the following. The resulting url appears as http://example.com/directory/?success=false
The problem I am having is that when a user attempts to submit the form again after correcting validation error the resulting url becomes http://example.com/directory/?success=false?success=true - I need it to clear any querystring first. How could I do this?
PHP
# Redirect user to error message
header('Location: ' . $_SERVER['HTTP_REFERER'] . '?success=false');
}
You could use explode() to break the $_SERVER['HTTP_REFERRER'] string to get rid of the existing $_GET arguments:
$bits = explode('?',$_SERVER['HTTP_REFERRER']);
$redirect = $bits[0];
# Redirect user to error message
header('Location: ' . $redirect . '?success=true');
How about something like this:
$i = strchr($_SERVER['HTTP_REFERER'], "?");
$address = substr($_SERVER['HTTP_REFERER'], 0, $i);
header('Location: ' . $address . '?success=false');

launch website url with array when value is empty

I am trying to make a little search game and i am creating a command line that opens websites in a iframe, in the middle of the page (i didn't add this iframe yet to the page). I must say that the command line field is also in an iframe.
You can see and test the page here: http://www.josdenhertog.nl/tnes/getin.php
Now the problem:
When you just use your mouse and just press it so that you see the cursor at the start in the text field and you press ENTER on your keyboard without typing any command in this line, then it goes black on the iframe and do not load anything.
This is the code what i have so far:
$urlList = array ('test' => 'commandline.php',
' ' => 'commandline.php',
' ' => 'commandline.php'
);
if (isset ($_POST['command']) && strlen($_POST['command']) > 0) {
# See if the command provided by the user exists in the list.
if (array_key_exists ($_POST['command'], $urlList)) {
#When Command exist.
header ("Location: " . $urlList[$_POST['command']]);
}
else {
# Command not found
header ("Location: commandline.php");
}
}
My question is now:
How do i make that when you press only ENTER without typing anything in the command line, load up ONLY the commandline.php webpage. Like that array variable: $urlList
I am terrible when it comes on questions, hopefully you understand what i mean :)
You need another else at the very end presumably. This will redirect back to commandline.php when $_POST['command'] is not set, or strlen() is <=0.
$urlList = array ('test' => 'commandline.php',
' ' => 'commandline.php',
' ' => 'commandline.php'
);
if (isset ($_POST['command']) && strlen($_POST['command']) > 0) {
# See if the command provided by the user exists in the list.
if (array_key_exists ($_POST['command'], $urlList)) {
#When Command exist.
header ("Location: " . $urlList[$_POST['command']]);
}
else {
# Command not found
header ("Location: commandline.php");
}
} else {
header ("Location: commandline.php");
}
die();
Add this to the end of the code shown above, all you need is an else statement since your check for command string length and isset are the ones behind your issue:
else {
header ("Location: commandline.php");
}

Form redirection with multiple variables

I found a wonderful example on form redirection when there is an error with the form elements. In validation.php the system checks if there is an error and if it's true it redirects the user to the form page.
My question is what if I have more than one form element?
As you see I renamed user_name to app_name and I added a new variable (adtext) so now I get two error messages when both form elements have some error (right now they not equal to a certain word), but I don't know what to do with the $query_string variable so the url would contain the second variable and its value as well.
This is how the url of the form page (adparameters.php) looks like when I click the submit button and there is an error with $appname:
/adparameters.php?appname=aa&error=App%20name%20is%20requiredAd%20text%20is%20required
<?php
# validate.php
$appname = trim($_POST['appname']);
$adtext = trim($_POST['adtext']);
$error = '';
if ($appname != 'myapp') $error = 'App name is required<br />';
if ($adtext != 'mytext') $error = $error . 'Ad text is required<br />';
$query_string = '?appname=' . $appname;
$server_dir = $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/\\') . '/';
header('HTTP/1.1 303 See Other');
if ($error != '') {
// Back to register page
$next_page = 'adparameters.php';
// Add error message to the query string
$query_string .= '&error=' . $error;
// This message asks the server to redirect to another page
header('Location: http://' . $server_dir . $next_page . $query_string);
}
// If Ok then go to confirmation
else $next_page = 'confirmation.php';
/*
Here is where the PHP sql data insertion code will be
*/
// Redirect to confirmation page
header('Location: http://' . $server_dir . $next_page . $query_string);
?>
The greatness of this code is that if I type something in the first input type object and it doesn't equal 'myapp' it is still filled with the text after redirection. That's what I want with the second object as well.
Best practice would be to send them in a $_SESSION.
session_start();
$_SESSION['form'] = array();
$_SESSION['form']['myapp'] = 'App Error Code';
$_SESSION['form']['adtext'] = 'AdText Error Code';
Then on the new page you would get the values as an array;
session_start();
$form_error = $_SESSION['form']['myapp'];
$form_error = $_SESSION['attext']['myapp'];
If you insist on using GET parameters why not append them on with the & character.
?field1=one&field2=two
I woldn't do it the way you do it but if you want it like that just change few things
if ($appname != 'myapp') $error = 'App name is required<br />';
if ($adtext != 'mytext') $error .= 'Ad text is required<br />';//note contatenation
$query_string = '?appname=' . $appname .'&addtext='.$adtext;

Categories