PHP Login $_SESSIONS w/user level support

Hello all, I’m a bit stuck with a PHP Sessions login page. The page itself works fine but I want it to redirect to different pages according to the users access level which on the SQL server is the ‘userlevel’ field. Currently, the levels are only 1, 2, and 3. 1 needs to go to userdata.php, 2 userdata2.php and 3 userdata3.php. The userdata.php files are identical for testing purposes. The code was a bit of a mess so I cleaned it up to the point the page is working again minus the redirection portion of course. Any help would be greatly appreciated, thanks.

login.php

<?php 
	require_once("functions.php");
	require_once("connection.php");
	session_start();
	if (logged_in() == true) {
		redirect_to("userdata.php");
	}
?>
<html>
<head>
</head>
<body>
<h1>User Login Form</h1>
<hr />
<!-- The HTML login form -->
	<form action="<?=$_SERVER['PHP_SELF']?>" method="post">
		Username: <input type="text" name="username" /><br />
		Password: <input type="password" name="password" /><br />
 
		<input type="submit" name="submit" value="Login" />

	</form>
<?php
if (isset($_POST['submit'])) {
	$username = $_POST['username'];
	$password = $_POST['password'];
 
	if ($mysqli->connect_errno) {
		echo "<p>MySQL error no {$mysqli->connect_errno} : {$mysqli->connect_error}</p>";
		exit();
	}
 
	$sql = "SELECT * from access WHERE username LIKE '{$username}' AND password LIKE '{$password}' LIMIT 1";
	$result = $mysqli->query($sql);
 
	if ($result->num_rows == 1) {
		
		$user = $result->fetch_array();
		$_SESSION['firstname'] = $user['firstname'];
		$_SESSION['user_id'] = $user['id'];
		$_SESSION['username'] = $user['username'];
		$_SESSION['userlevel'] = $user['userlevel'];		
 
		redirect_to("userdata.php?id={$_SESSION['user_id']}");
		
	} else {
		
		echo "<p><b>Error:</b> Username or Password Incorrect</p>";

	}
}
 
?>	
<hr />
</body>
</html>

functions.php

<?php
	function logged_in () {
		if (isset($_SESSION['username']) && isset($_SESSION['user_id'])) {
			return true;
		} else {
			return false;
		}
	}
 
	function redirect_to ($url) {
		header("Location: {$url}");
	}
?>

userdata.php

<?php 
require_once("functions.php");
require_once("connection.php");
session_start();
if (logged_in() == false) {
	redirect_to("login.php");
} else {
?>
 
<html>
<head>

	<script src="script.js" type="text/javascript"></script>
</head>
<body>
<h1>Logged in</h1>
<hr />
<?php

	if (isset($_GET['id']) && $_GET['id'] != "") {
		$id = $_GET['id'];
	} else {
		$id = $_SESSION['user_id'];
	}
 		$sql = "SELECT * FROM access WHERE id = {$id} LIMIT 1";
 
		if ($result = $mysqli->query($sql)) {
			$user = $result->fetch_array();
		} else {
			echo "<p>MySQL error no {$mysqli->errno} : {$mysqli->error}</p>";
			exit();
		}
 
		if ($result->num_rows == 1) {
 
			# echo the user profile data
			echo "<p>Userlevel: {$user['userlevel']}</p>";
			echo "<p>User First Name: {$user['firstname']}</p>";
			echo "<p>User ID: {$user['id']}</p>";
			echo "<p>Username: {$user['username']}</p>";
	
		} else { // 0 = invalid user id
			echo "<p><b>Error:</b> Invalid user ID.</p>";
		}
}
 
if (logged_in() == true) {
	echo '<a href="logout.php">Log Out</a>';
} else {
	echo '<a href="login.php">Login</a>';
}
?>
<hr />
</body>
</html>

You can’t do a header redirect when you have sent any data to the browser. So in your first bit of code, the first redirect might work, if the two included files above it don’t output anything to the browser. The rest won’t, because they are after output. You should be getting a “headers already sent” error message.

If you move the form processing in login.php to before the html output, that would probably help it and would be a more logical place for it to be - there’s no need to re-draw the form in the section where you’re processing the user input.

You’re vulnerable to SQL Injection attacks with that code, you should read up on the use of prepared statements. Also passwords should never ever be stored as plain text! They should always be hashed!

1 Like

droopsnoot-I moved the code as you had suggested, thanks for that, but i still can’t get the code for the if statement right. I’m not sure what I’m doing wrong but I keep getting an ‘unidentified userlevel’ error when I try to add it. Basically I have something to the extent of

if (($result->num_rows == 1) && ('userlevel' == '1')){		
		$user = $result->fetch_array();
		$_SESSION['firstname'] = $user['firstname'];
		$_SESSION['user_id'] = $user['id'];
		$_SESSION['username'] = $user['username'];
		$_SESSION['userlevel'] = $user['userlevel'];		 
		redirect_to("userdata.php?id={$_SESSION['user_id']}");

I know it’s screwed up I just can’t figure out what it needs to be. Thanks.

SpacePhoenix-Thanks, I’m still pretty new at PHP and I’m amazed I’ve gotten as far as I have with this. I’ve been readin up on sql injection and I’m pretty sure I can fix that part, thanks for looking out.

Because you are using SELECT * FROM my guess is that the field name is not the “userlevel” you think it is.

1 Like

How will this statement ever be true?

if (($result->num_rows == 1) && ('userlevel' == '1')){	

That is, you’re comparing the string “userlevel” to see if it’s the same as the string “1”. Which it isn’t.

If that’s a typo for the forum, and you meant to use $user['userlevel'] there, isn’t that the array that is fetched in the very next line, and therefore won’t exist at that point?

1 Like

Ya it was a typo, I’ve got to move some things around and reevaluate how I’ve written this whole thing.

I finally got it working! The code was all a** backwards and in the way wrong order. Thank you all so much for your help! Also turns out I only needed 2 userlevels so one less thing to worry about. Here’s what I finally came up with, thanks again!

<?php

if (isset($_POST['submit'])) {
	$username = $_POST['username'];
	$password = $_POST['password'];
 
	$sql = "SELECT * from access WHERE username LIKE '{$username}' AND password LIKE '{$password}' LIMIT 1";
	$result = $mysqli->query($sql);
 
	if ($result->num_rows == 1) {
		
		$user = $result->fetch_array();
		$_SESSION['firstname'] = $user['firstname'];
		$_SESSION['user_id'] = $user['id'];
		$_SESSION['username'] = $user['username'];
		$_SESSION['userlevel'] = $user['userlevel'];		
		
		if (($result->num_rows == 1) && ($user['userlevel'] == '1'))
		{	
 		redirect_to("userdata1.php?id={$_SESSION['user_id']}");
		}
		elseif (($result->num_rows == 1) && ($user['userlevel'] == '2'))
		{
		redirect_to("userdata2.php?id={$_SESSION['user_id']}");
		}
	}
		else {
		echo "<p><b>Error:</b> Username or Password Incorrect</p>";
			 }
}
?>

That’s good, but please heed the warning given by @SpacePhoenix in post #3. This is very vulnerable code.
Also I’m not sure about the use of “LIKE” in the query, would not = be more precise?

I am looking into securing it now, I just wanted to get it into a working format before adding another dimension to it. Also, is there an advantage to using = instead of ‘LIKE’? They appear to do the same thing but if one is better practice then going forward I’d like to stick with the best option. Thanks.

1 Like

Using = returns true only on an exact match.
https://dev.mysql.com/doc/refman/5.7/en/string-comparison-functions.html
LIKE can be used with wildcards for similar matches.

Things to remember when dealing with user input:-
Validate all user input.
Sanitise all user input.
http://php.net/manual/en/function.filter-input.php
When user input goes into an SQL query, always use prepared statements.
When echoing user input, escape special characters.

With passwords, always use hashing and the built in verify function.

2 Likes

Thank you all for the assistance It’s much appreciated.

The session_start() should be the first statement in the script or on the first line of the first included file.
It is good practice to have it included on all .php files.
A problem with redirection is that it redraws the full page resulting of the flickering as the page content is redrawn.
This can indicate an amateurish looking site. It is ok if this is the only redirect, but if you have a complicated site requiring several redirects it is a negative experience for the visitor and ensuring that there is no output before the redirection may become an issue.
Using jScript to bind an event listener to the login button which makes a xmlHttpRequest to the server which uses php to send the level page back as a response which js uses to replace the login form with the level n content results is a site where the header and footer remains in position and only that content which is different overwriting the superfluous code.
It is more work, but you will probably go down that track sooner or later.
I only say this because I started out using redirection and then stumbled upon jScript and then converted the site to jScript control.
More work, but a more polished interface.

Did you get the results you wanted? I had an issue redirecting one page too, so I echo’d a JS script that redirects to a page when some conditions are met.

Happy Coding!

Yes everything is working great now, just working on making it more secure. Thanks.

This problem can’t happen when using php header redirects. It simply won’t work if there is any output before the header.

This is just another case for the point of separating your concerns.
Separating your php processing away from your html templates will remove any of this kind of problem with pages failing because of html output happening before any session start or header.
What I do is get all the processing out of the way first, then include an html template, after that is done.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.