Member Reg & Login Script - mysqli_stmt_bind_param() Errors

Discussions of secure PHP coding. Security in software is important, so don't be afraid to ask. And when answering: be anal. Nitpick. No security vulnerability is too small.

Moderator: General Moderators

Post Reply
UniqueIdeaMan
Forum Contributor
Posts: 197
Joined: Wed Jan 18, 2017 3:43 pm

Member Reg & Login Script - mysqli_stmt_bind_param() Errors

Post by UniqueIdeaMan »

Hello Everyone!

What do you make out of my latest code update ?


Member Reg & Login script

config.php

Code: Select all

<?php

/*
*	ERROR HANDLING
*	ini_set('display_errors', 1);
*   ini_set('display_startup_errors', 1);

*	For All Error, Warning and Notice
*   error_reporting(E_ALL); OR error_reporting(-1);
*	For All Errors
*   error_reporting(E_ERROR);
*	For All Warnings
*   error_reporting(E_WARNING);
*	For All Notice
*   error_reporting(E_NOTICE);
*/
error_reporting(E_ALL);

// session start
session_start();

// include files
include 'conn.php';
include 'site_details.php';

// include functions
include 'functions.php';

?>

functions.php

Code: Select all

<?php
// functions file

/*
* check if user is logged by checking if session named "user" isset
* return true if session "user" exists or false if not exists
*/
function is_logged() {
	if (isset($_SESSION["user"]) && !empty($_SESSION["user"])) {
		return true;
	} else {
		return false;
	}
}
?>

site_details.php

Code: Select all

<?php

$site_name = "Programmer's Haven";
$site_domain = "domain.com";
$site_admin_email = "programmers_haven_admin@domain.com";

?>

reg.php

Code: Select all

<?php

// config.php contains reference to site_details.php (which contains details such as site name, site domain, webmaster email) and conn.php (which contains db connection details).
include 'config.php';

// Check if user is already logged in or not.
if (is_logged() === true) {
	die("You are logged in, can't register.");
}

if ($_SERVER['REQUEST_METHOD'] == "POST")
{
	if (isset($_POST["username"]) && 
	   isset($_POST["password"]) &&
	   isset($_POST["password_confirmation"]) && 
	   isset($_POST["email"]) && 
	   isset($_POST["email_confirmation"]) && 
	   isset($_POST["forename"]) && 
	   isset($_POST["gender"]) &&
	   isset($_POST["surname"])) {

		// Create random hash for email confirmation.
	   	$member_registration_random_numbers = sha1(mt_rand(5, 30));

	   	// Account activation link that will verify email.
		$account_activation_link = "http://www.'".$site_domain."'.com/$site-name/activate_account.php?email='".$_POST['email']."'&hash='".$member_registration_random_numbers."'";

   		// Remove space in start of string.
   		/*
		*	Passwords and email are leaved unescaped here because
		*	if you put them into mysqli_real_escape_string they are not empty.
   		*/
        $username 	= trim(mysqli_real_escape_string($conn, $_POST["username"]));
		$password 	= $_POST["password"];
		$password2 	= $_POST["password_confirmation"];
        $forename 	= trim(mysqli_real_escape_string($conn, $_POST["forename"]));
        $surname 	= trim(mysqli_real_escape_string($conn, $_POST["surname"]));
		$gender 	= trim(mysqli_real_escape_string($conn, $_POST["gender"]));
        $email 		= $_POST["email"];
        $email_confirmation = $_POST["email_confirmation"];
        $email2 	= trim(mysqli_real_escape_string($conn, $email)); // Escaped email for inserting into database
        $activation = 0; // 1 = active | 0 = not active

        // Hashed password.
        $hashed_pass = password_hash($password, PASSWORD_DEFAULT); 
	
        // Select username and email to check if they exist or not.
		$stmt = mysqli_prepare($conn, "SELECT usernames, emails FROM users WHERE usernames = ? OR emails = ?");
		mysqli_stmt_bind_param($stmt, 'ss', $username, $email);
		mysqli_stmt_execute($stmt);
		$result = mysqli_stmt_get_result($stmt);

        $row = mysqli_fetch_array($result, MYSQLI_ASSOC);

		// check if username is registered
		if ($row['Usernames'] == $username) {
			$_SESSION['error'] = "That username is already registered.";
		// check if username is between 6 and 30 characters long
		} elseif (strlen($username) < 6 || strlen($username) > 30) {
			$_SESSION['error'] = "Username must be between 6 and 30 characters long.";
		// check if email is registered
		} elseif ($row['Emails'] == $email) {
			$_SESSION['error'] = "That email is already registered.";
		// check if emails match
		} elseif ($email != $email_confirmation) {
			$_SESSION['error'] = "Emails don't match.";
		// check if email is actual email
		} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
			$_SESSION['error'] = "Invalid email format.";
		// check if passwords match
		} elseif ($password != $password2) {
			$_SESSION['error'] = "Passwords don't match.";
		// check if password lenght is between 6 and 30 charaters long
		} elseif (strlen($password) < 6 || strlen($password) > 30) {
			$_SESSION['error'] = "Password must be between 6 and 30 characters long.";
		} else {

			// insert query with mysqli prepared statement
			$stmt = mysqli_prepare($conn, "INSERT INTO users(usernames, passwords, emails, forenames, surnames, genders, account_activation_codes, account_activations) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
			mysqli_stmt_bind_param($stmt, 'sssssssi', $username, $hashed_pass, $email2, $forename, $surname, $gender, $member_registration_random_numbers, $activation);
			mysqli_stmt_execute($stmt);

			// check if query is inserted
			if (mysqli_stmt_insert_id($stmt)) {
				echo "<h3 style='text-align:center'>Thank you for your registration.<br /> Redirecting to login page ...</h3>";

				// Redirect to login page after 5 seconds
				header("refresh:5;url=login.php");

				// Empty $_SESSION['error'] variable so no more in use, its empty now.
				unset($_SESSION['error']);
				unset($_POST);
				exit(); 

				// Email sent to new user with account activation link.
				$to = $email;
			    $subject = "Your '".$site_name."' Account Activation!";
			    $body = $forename.' '.$surname."\n\n You need to click the following link to confirm your email address and activate your account.\n\n\
			    $account_activation_link";
				$from = $site_admin_email;
			    $headers = "from: " . $from;
			
			    if (mail($to,$subject,$body,$headers)) {
			    	$_SESSION['error'] = "Registration sucessfuly. Check your email for further instructions!";
			    } else {
			    	$_SESSION['error'] = "Email not sent, please contact website administrator.";
			    }
			    */
			} else {
				$_SESSION['error'] = "There was a problem with registering, please try again.";
			}

	    }
	}
}


?>
<!DOCTYPE html>
<html>
	<head>
		<title><?php $site_name ?> Signup Page</title>
	</head>
<body>
<div class ="container">

<?php

// error messages
if (isset($_SESSION['error']) && !empty($_SESSION['error'])) {
	echo '<p style="color:red;">'.$_SESSION['error'].'</p>';
}

?>

<form method="post" action="">
	<center><h2>Signup Form</h2></center>
	<div class="form-group">
		<center><label>Username:</label>
		<input type="text" placeholder="Enter a unique Username" name="username" required [A-Za-z0-9] value="<?php if(isset($_POST['username'])) { echo htmlentities($_POST['username']); }?>"></center>
	</div>
	<div class="form-group">
		<center><label>Password:</label>
		<input type="password" placeholder="Enter a new Password" name="password" required [A-Za-z0-9]></center>
	</div>
	<div class="form-group">
		<center><label>Repeat Password:</label>
		<input type="password" placeholder="Repeat a new Password" name="password_confirmation" required [A-Za-z0-9]></center>
	</div>
	<div class="form-group">
		<center><label>First Name:</label>
		<input type="text" placeholder="Enter your First Name" name="forename" required [A-Za-z] value="<?php if(isset($_POST['forename'])) { echo htmlentities($_POST['forename']); }?>"></center>
	</div>
	<div class="form-group">
		<center><label>Surname:</label>
		<input type="text" placeholder="Enter your Surname" name="surname" required [A-Za-z] value="<?php if(isset($_POST['surname'])) { echo htmlentities($_POST['surname']); }?>"></center>
	</div>
	<div class="form-group">
		<center><label>Gender:</label>
		<input type="radio" name="gender" value="male" <?php if(isset($_POST['gender'])) { echo 'checked'; }?> required>Male<input type="radio" name="gender" value="female" <?php if(isset($_POST['gender'])) { echo 'checked'; }?> required>Female</center>
	</div>
	<div class="form-group">
		<center><label>Email:</label>
		<input type="email" placeholder="Enter your Email" name="email" required [A-Za-z0-9] value="<?php if(isset($_POST['email'])) { echo htmlentities($_POST['email']); }?>"></center>
	</div>
	<div class="form-group">
		<center><label>Repeat Email:</label>
		<input type="email" placeholder="Repeat your Email" name="email_confirmation" required [A-Za-z0-9] value="<?php if(isset($_POST['email_confirmation'])) { echo htmlentities($_POST['email_confirmation']); }?>"></center>
	</div>
	<center><button type="submit" class="btn btn-default" name="submit">Register!</button></center>
	<center><font color="red" size="3"><b>Already have an account ?</b><br><a href="login.php">Login here!</a></font></center>

</form>

</div>
</body>
</html>

login.php

Code: Select all

<?php
include 'config.php';

// check if user is already logged in
if (is_logged() === true) {
	die("You are already logged in.");
}

if ($_SERVER['REQUEST_METHOD'] == "POST")
{
	if (isset($_POST["username_or_email"]) && isset($_POST["password"])) {

		$username = $_POST["username_or_email"];
		$email = $_POST["username_or_email"];
		$password = $_POST["password"];

		$stmt = mysqli_prepare($conn, "SELECT Usernames, Passwords, Emails, Account_Activation_Codes, Account_Activations FROM users WHERE Usernames = ? OR Emails = ?");
		mysqli_stmt_bind_param($stmt, 'ss', $username, $email);
		mysqli_stmt_execute($stmt);
		$result = mysqli_stmt_get_result($stmt);

        $row = mysqli_fetch_array($result, MYSQLI_ASSOC);
		
		// check for username and password matching
        if ($username == $row['Usernames']  || $email == $row['Emails'] && password_verify($password, $row['Passwords'])) {

        	/* 
        	* Check if user has activation link in database, if it has then he has not activated his account
        	* or
			* check if user Activation_Accounts is set to 1 its active and 0 is not active.
			*/
        	if ($row['Account_Activation_Codes'] != '' || $row['Account_Activations'] == '0') {
        		$error = "You didn't activate your account. Please check your email.";
        	} else {		

	        	// if remember me check box is checked set cookie
	        	if (isset($_POST['remember']) && $_POST['remember'] == "on") {
	        		/*
					* if you want to set cookie, set only hash and store it into database
					* when you come on login page you need to check  if that hash from cookie exists in database
					* if it exist just start session
					* NEVER STORE USERNAMES, EMAILS, PASSWORDS AND OTHER USER INFORMATION IN COOKIE
					*/

	        		//setcookie("username_or_email", $username_or_email, time()+ (10 * 365 * 24 * 60 * 60));
					//setcookie("password", $password, time()+ (10 * 365 * 24 * 60 * 60));
	        	} else {
	        		// start session
	        		$_SESSION["user"] = $username;
	        		$_SESSION["user"] = $email;

	        		// redirect to member page
	        		header("Location: home.php");
	        		exit();
	        	}

	        }
					
	    } else {
            $error = "Invalid username or password.";
        }		
	}
}	

?>
<!DOCTYPE html>
<html>
	<head>
		<title><?php $site_name?> Member Login Page</title>
	</head>
<body>
<div class="container">
	<form method="post" action="">
	<h3 style="text-align:center;"><?php $site_name ?> Member Login Form</h3>

	<?php if(!empty($error)) { echo '<p style="color:red; text-align:center;">'.$error.'</p>'; } ?>

		<div class="form-group">
			<center><label>Username/Email:</label>
			<input type="text" placeholder="Enter Username or Email" name="username_or_email" required></center>
		</div>
		<div class="form-group">
			<center><label>Password:</label>
			<input type="password" placeholder="Enter password" name="password" required></center>
		</div>
		<div class="form-group">
			<center><label>Remember Login Details:</label>
			<input type="checkbox" name="remember"></center>
		</div>
		<div class="form-group">
			<center><input type="submit" name="submit" value="Login" class="button button-success"></center>
		</div>

		<div class="form-group">
			<center><font color="red" size="3"><b>Forgot your password ?</b><br><a href="member_login_password_reset.php">Reset it here!</a></font></center>
			<center><font color="red" size="3"><b>Not registered ?</b><br><a href="register.php">Register here!</a></font></center>
		</div>
	</form>
</div>
</body>
</html>
I am getting these absurd errors on reg.php:

Notice: Undefined variable: site in C:\xampp\htdocs\...\...\register.php on line 24
Warning: mysqli_stmt_bind_param() expects parameter 1 to be mysqli_stmt, boolean given in C:\xampp\htdocs\....\...\register.php on line 78
Warning: mysqli_stmt_execute() expects parameter 1 to be mysqli_stmt, boolean given in C:\xampp\htdocs\...\...\register.php on line 79
Warning: mysqli_stmt_insert_id() expects parameter 1 to be mysqli_stmt, boolean given in C:\xampp\htdocs\...\...\register.php on line 82


And these errors on the login.php:

Warning: mysqli_stmt_bind_param() expects parameter 1 to be mysqli_stmt, boolean given in C:\xampp\htdocs\...\...\login.php on line 18
Warning: mysqli_stmt_execute() expects parameter 1 to be mysqli_stmt, boolean given in C:\xampp\htdocs\...\...\login.php on line 19
Warning: mysqli_stmt_get_result() expects parameter 1 to be mysqli_stmt, boolean given in C:\xampp\htdocs\...\...\login.php on line 20
Warning: mysqli_fetch_array() expects parameter 1 to be mysqli_result, null given in C:\xampp\htdocs\...\...\login.php on line 22


Apart from the error codes. I believe the script is now sql injection free and the password hashing is sound. What is your opinion ?
Last edited by requinix on Tue Apr 25, 2017 2:41 am, edited 1 time in total.
Reason: [php] -> [syntax=php]
User avatar
requinix
Spammer :|
Posts: 6617
Joined: Wed Oct 15, 2008 2:35 am
Location: WA, USA

Re: Member Reg & Login Script - mysqli_stmt_bind_param() Err

Post by requinix »

Use mysqli->error to get an error message explaining why preparing the statement failed.

Meanwhile the first undefined $site error is something completely different. Is it supposed to be $site_name?
UniqueIdeaMan
Forum Contributor
Posts: 197
Joined: Wed Jan 18, 2017 3:43 pm

Re: Member Reg & Login Script - mysqli_stmt_bind_param() Err

Post by UniqueIdeaMan »

requinix wrote:Use mysqli->error to get an error message explaining why preparing the statement failed.

Meanwhile the first undefined $site error is something completely different. Is it supposed to be $site_name?
You are right. That was a typo on my part. Should've been $site_name. ANyway, fixed it about 3 nights ago.
Post Reply