I searched for this question but couldn't find it.
I have made a create user page that will allow the user to create an account on my page using a username. Usernames can be any combination of letters and numbers. When they create the user, it is supposed to call the same page, then redirect the user to the main page when it sees that the session variable is now set.
When I create a user with only letters in the username, it works fine and redirects them to the index page. However, when I create a user such as "student1" it will not set the session variable and therefore not redirect them.
You can try it yourself at http://collinmath.com/accounts/create.php to see what I mean. (Just don't use real info since I haven't set up the SSL yet)
<?php
// call the register() function if register_btn is clicked
if (isset($_POST['register_btn'])) {
// Set variables equal to POST data
$login_name = $_POST['username'];
$first_name = $_POST['firstname'];
$last_name = $_POST['lastname'];
$email = $_POST['email'];
$role = $_POST['role'];
$pwd1 = $_POST['password_1'];
$pwd2 = $_POST['password_2'];
register();
}
// Register function will check the input and add the user if
// the input is accepted
function register() {
global $login_name;
global $first_name;
global $last_name;
global $email;
global $role;
global $errors;
global $connection;
global $pwd1;
global $pwd2;
global $hostname;
global $username;
global $password;
global $dbname;
// Connect to database
$connection = mysqli_connect($hostname, $username, $password);
mysqli_select_db($connection, $dbname);
// Check that username contains only letters and number
if (preg_match('/[^A-Za-z0-9]/', $login_name)) {
array_push($errors, "Username must contain only letters and/or numbers");
} else {
$login_name = strtolower($login_name);
}
// Sanitize SQL data
$first_name = mysqli_real_escape_string($connection, $first_name);
$last_name = mysqli_real_escape_string($connection, $last_name);
// Validate registration input and generate error log if there are issues
// Check if username is taken or empty
if (strlen($login_name) > 4) {
$query = "SELECT `User_Login` AS `Login` FROM `CMP_Users` WHERE `User_Login`=?";
$mysqli = new mysqli($hostname, $username, $password, $dbname);
$mysqli->set_charset("utf8");
$stmt = $mysqli->prepare($query);
$stmt->bind_param("s", $login_name);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
if ($row[Login]) {
array_push($errors, "That username is taken");
}
} else {
array_push($errors, "Username must be at least 5 characters long");
};
if (strlen($login_name) > 16) {
array_push($errors, "Username must be 16 characters or less");
}
// Check First name
if ($first_name) {
if (preg_match('/[^A-Za-z\'\-\s]/', $first_name) || !preg_match('/[A-Za-z]/i', $first_name)) {
array_push($errors, "First Name is not valid");
}
if (strlen($first_name) > 15) {
array_push($errors, "First name must be 15 characters or less");
}
} else {
array_push($errors, "Must enter a first name");
}
//Check Last name
if ($last_name) {
if (preg_match('/[^A-Za-z\'\-\s]/', $last_name) || !preg_match('/[A-Za-z]/i', $last_name)) {
array_push($errors, "Last Name is not valid");
}
if (strlen($last_name) > 25) {
array_push($errors, "Last name must be 25 characters or less");
}
} else {
array_push($errors, "Must enter a last name");
}
// Validate e-mail
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
array_push($errors, "Please enter a valid e-mail address");
}
if (strlen($email) > 50) {
array_push($errors, "E-mail address must be 50 characters or less");
}
// Check if role is legal
$role_value = 0;
if ($role == 'student') {
$role_value = 1;
} else if ($role == 'teacher') {
$role_value = 2;
} else {
array_push ($errors, "No role selected");
}
// Check if passwords match
if ($pwd1 != $pwd2) {
array_push($errors, "Passwords do not match");
} else {
// Check if passwords meet criteria
if (!preg_match('/\W/', $pwd1) || !preg_match('/[0-9]/', $pwd1) ||
strlen($pwd1) < 10) {
array_push($errors, "Password is not valid");
}
}
// If there are no errors, commit results to DB and create session
if (empty($errors)) {
// Hash passwords for DB storage
$pwd1 = password_hash($login_name . $_POST['password_1'], PASSWORD_DEFAULT);
/*
THIS WILL NEED TO BE UPDATED WHEN E-MAIL VALIDATION IS IMPLEMENTED
*/
// Create query for inserting new data
$add_user_query = "INSERT INTO `CMP_Users` (User_First_Name, User_Last_Name, "
. "User_Login, User_Email, User_Password, User_Role, User_Created) VALUES "
. "(?, ?, ?, ?, ?, ?, NOW())";
$mysqli_add_user = new mysqli($hostname, $username, $password, $dbname);
$mysqli_add_user->set_charset("utf8");
$stmt_add_user = $mysqli_add_user->prepare($add_user_query);
$stmt_add_user->bind_param("sssssi", $first_name, $last_name, $login_name, $email, $pwd1, $role_value);
$stmt_add_user->execute();
// Set session variables
$_SESSION['username'] = $login_name;
$_SESSION['role'] = $role_value;
$_SESSION['email'] = $email;
$_SESSION['fname'] = $first_name;
$_SESSION['lname'] = $last_name;
$connection->close();
header('Location: http://www.collinmath.com/mathpages/index.php');
exit();
}
// Close db connection
$connection->close();
}
// Check whether the user is already logged in
// and redirect them to the main user page if they are
if (isset($_SESSION['username'])) {
header('Location: http://www.collinmath.com/mathpages/index.php');
exit();
}
?>
UPDATE:
So, I changed a bunch of the code and tinkered with the php.ini file but I'm still having problems. When I look at my cookies, I see the cookie is there. I see the file is created in the sessions folder and that the variables are set in that file, but there is still no session info when I do a var_dump.
My session_save_path and var_dump shows this:
/home/[myname]/sessions/
array(0) { }
and the file that is created in my sessions folder looks like this:
username|s:7:"testerz";role|i:1;email|s:19:"email#email.com";fname|s:4:"First";lname|s:6:"Name";
Problem Fix:
This is work in progress from the long comment discussion and will be updated as we go
From what you've told me the logic process of your situation is impossible.
RE: Your update:
Then if the session is definitely being written then there are three possible options:
1) there is an ini_set() or a directory-local .ini file which is changing the session name so data from one directory is not being recognised in another, as they're looking in different sessions.
2) You have a spelling or casing issue of your $_SESSION keys.
3) session_start() has not been initiated.
Furter debugging and Solution:
Whenever you var_dump your session data, and it's turning up blank; add these lines:
error_log(print_r(session_status()."<BR>",true));
error_log(print_r(session_name()."<BR>",true));
error_log(print_r($_SESSION,true)); //your original output.
Add this code block to both your data-in page (create.php) and the destination page that is failing to show certain sessions.
If the above are always exactly the same (and they may be if , as you say, some data does "work".
Then the answer is that you definitely absolutely have some lines in your code that change the session values. The symptoms look like you've got a screwed up REGEX preg_ function somewhere. Again; use your PHP Error Log to check these things out.
General Fixes:
Quote your array keys; $row[Login] should be `$row['Login']
Use a single MySQLi connection, of a single type
That type should be the Object Orientated approach (->)
Do not use real_escape_string for Object Orientated MySQL connections.
Use Multibyte PHP String functions
Use UTF8mb4 MySQLi connection character sets, and the same in your tables and columns.
Tidy up your code and your logic process, you've made a funtion but the function always runs so it has no benefit being a function - it may as well just be straight code.
Don't use globals
MySQL does not care about new lines so you don't need to concatenate the SQL strings.
Wow, this was the most random reason...
It turns out the problem was my php.ini file, but not for the reason I thought. I had set the session.cookie_domain value to "collinmath.com" but needed to add a period to the beginning of collinmath. I changed the session.cookie_domain value to ".collinmath.com" and it fixed the problem. I have no idea why this worked, but it did.
Related
I have created a website that has a basic registration and login system, I have pages that I only want admins to access.
My database for the accounts has a role column with 1 user assigned as admin and the other assigned as user
AUTHENTICATE.PHP
<?php
session_start();
// Change this to your connection info.
$DATABASE_HOST = 'localhost';
$DATABASE_USER = 'root';
$DATABASE_PASS = '';
$DATABASE_NAME = 'feedbackdb';
// Try and connect using the info above.
$con = mysqli_connect($DATABASE_HOST, $DATABASE_USER, $DATABASE_PASS, $DATABASE_NAME);
if ( mysqli_connect_errno() ) {
// If there is an error with the connection, stop the script and display the error.
exit('Failed to connect to MySQL: ' . mysqli_connect_error());
}
// Now we check if the data from the login form was submitted, isset() will check if the data exists.
if ( !isset($_POST['username'], $_POST['password']) ) {
// Could not get the data that should have been sent.
exit('Please fill both the username and password fields!');
}
// Prepare our SQL, preparing the SQL statement will prevent SQL injection.
if ($stmt = $con->prepare('SELECT id, password FROM accounts WHERE username = ?')) {
// Bind parameters (s = string, i = int, b = blob, etc), in our case the username is a string so we use "s"
$stmt->bind_param('s', $_POST['username']);
$stmt->execute();
// Store the result so we can check if the account exists in the database.
$stmt->store_result();
if ($stmt->num_rows > 0) {
$stmt->bind_result($id, $password);
$stmt->fetch();
// Account exists, now we verify the password.
// Note: remember to use password_hash in your registration file to store the hashed passwords.
if (password_verify($_POST['password'], $password)) {
// Verification success! User has loggedin!
// Create sessions so we know the user is logged in, they basically act like cookies but remember the data on the server.
session_regenerate_id();
$_SESSION['loggedin'] = TRUE;
$_SESSION['name'] = $_POST['username'];
$_SESSION['id'] = $id;
$_SESSION['admin'] = true/false;
header('location: home.php');
} else {
echo 'Incorrect password!';
}
} else {
echo 'Incorrect username!';
}
$stmt->close();
}
?>
And what I use to check:
<?php
session_start();
if(isset($_SESSION['admin'], $_SESSION['admin'])){
header('Location: index.php');
exit;
}
?>
that's the code I'm using in the page,
the problem I have is it doesn't matter who I log in as it always redirects, whereas I want the page to be accessible for admins but not users.
First, you need to alter your query to return the role column.
if ($stmt = $con->prepare('SELECT id, password, role FROM accounts WHERE username = ?'))
Next, you need to bind that value the same way you do $id and $password.
$stmt->bind_result($id, $password, $role);
Next, inside of your password_verify() block where you assign the other $_SESSION variables, set a role variable.
$_SESSION['loggedin'] = TRUE;
$_SESSION['name'] = $_POST['username'];
$_SESSION['id'] = $id;
$_SESSION['role'] = $role;
Now, on any page you want, you can block access to anyone who isn't an admin.
if(empty($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
//block user access
die("You do not have permission to view this page.");
}
If you want to show something on a page which is only visible to admins without blocking all users from the page entirely, you could do
if(!empty($_SESSION['role']) && $_SESSION['role'] == 'admin') {
echo "Only admins can see this text.";
}
Variable Cleanup
I recommend instead of creating multiple different session variable, you create a single user session variable containing an array of data you may need. I recommend this because it is cleaner, easier to manage, and easier to use later in your code.
Basically, replace all of your $_SESSION variable declarations with this:
$_SESSION['user'] = [
'loggedin' => true,
'name' => $_POST['username'],
'id' => $id,
'role' => $role
];
Then, to check if the user is an admin you would do something like this:
if(empty($_SESSION['user']) || $_SESSION['user']['role'] !== 'admin') {
//block user access
die("You do not have permission to view this page.");
}
And to show something for only admins without blocking all users from the page entirely:
if(!empty($_SESSION['user']) && $_SESSION['user']['role'] == 'admin') {
echo "Only admins can see this text.";
}
NOTE: Using MySQL Root user for website DB access is extremely unwise
and unsafe. Don't do it. Make a specific MySQL user only with limited
permissions on that data that they require
from Martin in the
comments.
For some reason my last two variables will not input into the database. I am referring to $verifyKey and $keyExpire. Here is my code with comments. I am adding the entire page to make sure I don't have the wrong character somewhere it isn't supposed to be. Effectively this is a registration page that inserts the information into the database and gives a verification key for email verification later.
I have the fields matched up with the code in the database and they are set to longtext and text. I don't want to insert directly as I am trying to get this method to work with all 5 variables.
<?php
// This is not seen by the end user so this file is placed in the unseen folder
// Check if the user used the sign up button
if (isset($_POST['signup-submit'])) {
// uses the database handler
require 'dbh.php';
$username=$_POST['uid'];
$email=$_POST['mail'];
$password=$_POST['pwd'];
$passwordcnfrm=$_POST['pwd-cnfrm'];
$verifyKey=md5(time().$username);
$keyExpire=date("U")+ 86400;
// Checks to see if any field are empty
if(empty($username)||empty($email)||empty($password)||empty($passwordcnfrm)) {
// This header returns the username and/or email address so the user doesn't have to retype it
header("Location:../signup.php?error=emptyfields&uid=".$username."&mail=".$email);
exit();
}
// Checks if both the user and email are invalid
else if (!filter_var($email, FILTER_VALIDATE_EMAIL)&&!preg_match("/^[a-zA-Z0-9]*$/",$username)) {
header("Location:../signup.php?error=invalidmailuid");
exit();
}
// Checks if the email is valid
else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
header("Location:../signup.php?error=invalidmail&uid=".$username);
exit();
}
// Checks if the username is valid.
else if (!preg_match("/^[a-zA-Z0-9]*$/",$username)) {
header("Location:../signup.php?error=invaliduid&mail=".$email);
exit();
}
// Checks to see if the password and confirm password match
else if ($password !== $passwordcnfrm){
header("Location:../signup.php?error=passwordcheck&uid=".$username."&mail=".$email);
exit();
}
// Checks to see if the username is already in use or password is invalid
else {
$sql = "SELECT uidUsers FROM users WHERE uidUsers=?";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt,$sql)) {
header("Location:../signup.php?error=sqlerror");
exit();
}
else {
mysqli_stmt_bind_param($stmt,"s",$username);
mysqli_stmt_execute($stmt);
mysqli_stmt_store_result($stmt);
$resultCheck = mysqli_stmt_num_rows();
if ($resultCheck < 0){
header("Location:../signup.php?error=usertaken&mail=".$email);
exit();
}
else {
//Inserts into database
$sql = "INSERT INTO users (uidUsers,emailUsers,pwdUsers,verify,expires) VALUES (?,?,?,?,?);";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt,$sql)) {
header("Location:../signup.php?error=sqlerror");
exit();
}
else {
$hashedPwd =password_hash($password,PASSWORD_DEFAULT);
mysqli_stmt_bind_param($stmt,"sssss",$username,$email,$hashedPwd,$verifyKey,$keyExpire);
mysqli_stmt_execute($stmt);
header("Location:../signup.php?signup=success");
}
}
}
}
//Closes session to db
mysqli_stmt_close($stmt);
mysqli_close($conn);
}
else {
header("Location:../signup.php");
}
In description of mysqli_stmt::bind_param there is this criptic note:
If data size of a variable exceeds max. allowed packet size
(max_allowed_packet), you have to specify b in types and use
mysqli_stmt_send_long_data() to send the data in packets.
http://php.net/manual/en/mysqli-stmt.bind-param.php
Practically it means that for every longtext (and blob) field you have to additionally call mysqli_stmt_send_long_data after mysqli_stmt_bind_param. Otherwise you have this strange behavious when everything works ok, but fields are not set.
I tried to build a sign-up for a blog I'm creating to get experience with php, but unfortunately I can't seem to get "errors" working and I also haven't found anything useful for me on the internet.
With these functions I want to check if a registration is Valid ( username not already used, confirmation of password is the same, every field is filled)
public function isEmpty($username, $password, $pwagain){
if (empty($username) OR empty($password) OR empty($pwagain)) {
return true;
} else {
return false;
}
}
public function isAssigned($username)
{
if (!empty($this->userRepository-
>isUsernameAssigned($username))) {
return true;
} else {
return false;
}
}
public function isNotSame($password, $pwagain)
{
if ($password != $pwagain) {
return true;
} else {
return false;
}
}
public function isValid($username, $password, $pwagain)
{
$hashedpassword = password_hash($password, PASSWORD_DEFAULT);
$newUser = $this->userRepository->newUser($username,
$hashedpassword);
$_SESSION['login'] = $username;
return $newUser;
}
Here is the code that declares which error should be given, I don't know how to put them together so that the $errors does not get overwritten or something like that
public function sign()
{
$categories = $this->categoryRepository->allCategories();
$error = "";
if(!empty($_POST['username']) AND !empty($_POST['password'])
AND !empty($_POST['pwagain'])) {
$username = $_POST['username'];
$password = $_POST['password'];
$pwagain = $_POST['pwagain'];
if ($this->signInService->isEmpty($username, $password,
$pwagain)) {
$error = "Please fill in all fields.";
}
if ($this->signInService->isAssigned($username)) {
$error = "Username provided is already in use.";
}
if ($this->signInService->isSame($password, $pwagain)) {
$error = "The passwords do not match.";
}
if ($this->signInService->isValid($username, $password,
$pwagain)) {
header("Location: index");
return;
} else {
$error = "";
}
}
$this->render("user/sign", [
'error' => $error,
'categories' => $categories,
]);
}
That's at the beginning of my sign.php to echo the right error when the sign up's not valid.
<?php if(!empty($error)): ?>
<p class="error">
<?php echo e($error); ?>
</p>
<?php endif; ?>
I really don't know how to put the if's to get it working, at the moment it immediately puts the user in the database even though the username is already used or the passwords do not match. It also shows none of the (in the controller declared) $errors.
The functions (isAssigned etc) should not be the problem since they've worked before.
I would really appreciate it if someone could help me with this.
It appears that no matter what happens with the business logic validations (email must be new, user must have entered an email, password and password verification must both be set and not empty) is not driving any decision on your code to not call the function that actually creates a new user in the database ($this->signInService->isValid) so even if one or more of those validations fail, the insertion is done anyway.
The above raises multiple issues:
1.- if the email must be unique in the database but the insertion succeeds anyway, you should alter your user table and have the field where the email is stored flagged as UNIQUE (I'm assuming mySQL, postgres or similar.. not familiar with how other database engines would work). Even if everything else if left the same, this would prevent the new record insertion which, together with the following, would produce an error to be displayed:
if ($this->signInService->isValid($username, $password,
$pwagain))
{
header("Location: index");
return;
}
else
{
$error = "Insertion aborted. Email was duplicated";
}
Now, the above is just an additional protection layer for the duplicate email case. You shouldn't just rely on this so:
2.- Only attempt the insertion if all validations have passed. Otherwise skip right into the error output
$abort = false;
$error = '';
if ($this->signInService->isEmpty($username, $password,
$pwagain))
{
$error .= "Please fill in all fields.";
$abort = true;
}
if ($this->signInService->isAssigned($username))
{
$error .= "Username provided is already in use.";
$abort = true;
}
if ($this->signInService->isSame($password, $pwagain))
{
$error .= "The passwords do not match.";
$abort = true;
}
if ($abort == false)
{
if ($this->signInService->isValid($username, $password,
$pwagain))
{
header("Location: index");
return;
}
else
{
$error = "Insertion aborted. Email was duplicated";
}
}
$this->render("user/sign", [
'error' => $error,
'categories' => $categories,
]);
what the above does is:
a. first assume all checks will pass, so default to "not aborting" and set an empty error message (this prevents PHP warnings for the first concatenation.. PHP will warn if you are concatenating a string into a previously non-existing variable
b. run all checks. If any of those checks fail, flag the abortion by setting $abort = true; and concatenate the specific message to the error variable (so, if multiple checks fail, you can inform them all to the user
c. check if the abort flag is set to true and then decide if attempting the insert or just outputting the errors. This is important because a successful insert sends the user a redirect header, which halts the execution of everything below it (which is why you never got to the error message displaying)
d. finally, if validations passed but the insert failed, upon exiting the final if you'll redirect the user to the error output (note that I didn't use an else clause precisely to allow that a user input not flagged for abortion can re-join the flow that would have been followed by an abort flag
I am trying to find how to check if a variable called active is equal to 1. My attempt at the function is below:
function login($email, $password, $mysqli) {
// Using prepared statements means that SQL injection is not possible.
if ($stmt = $mysqli->prepare("SELECT id, username, password, salt, active
FROM members
WHERE email = ? LIMIT 1")) {
$stmt->bind_param('s', $email); // Bind "$email" to parameter.
$stmt->execute(); // Execute the prepared query.
$stmt->store_result();
// get variables from result.
$stmt->bind_result($user_id, $username, $db_password, $salt, $active);
$stmt->fetch();
// hash the password with the unique salt.
$password = hash('sha512', $password . $salt);
if ($stmt->num_rows == 1) {
// If the user exists we check if the account is locked
// from too many login attempts
if (checkbrute($user_id, $mysqli) == true) {
// Account is locked
// Send an email to user saying their account is locked
return false;
} else {
// Check if the password in the database matches
// the password the user submitted.
if ($db_password == $password) {
// Password is correct!
// Get the user-agent string of the user.
$user_browser = $_SERVER['HTTP_USER_AGENT'];
// XSS protection as we might print this value
$user_id = preg_replace("/[^0-9]+/", "", $user_id);
$_SESSION['user_id'] = $user_id;
// XSS protection as we might print this value
$username = preg_replace("/[^a-zA-Z0-9_\-]+/", "", $username);
$_SESSION['username'] = $username;
$_SESSION['login_string'] = hash('sha512', $password . $user_browser);
} else {
// Password is not correct
// We record this attempt in the database
$now = time();
if (!$mysqli->query("INSERT INTO login_attempts(user_id, time)
VALUES ('$user_id', '$now')")) {
header("Location: ../error?err=Database error: login_attempts");
exit();
}
return false;
}
}
} else {
// No user exists.
return false;
}
} else {
// Could not create a prepared statement
header("Location: ../error?err=Database error: cannot prepare statement");
exit();
}
}
I assume that where I added active to the $mysqli->prepare statement is correct.
What I want to do is if the user got their password correct I would query the MySQL table to see if his account is active(1) or not active(0). If it is set to 0 it logs in with no error. However in my process_login.php file it logs the user in if it is (0) but with index.php?err=1
<?php
include_once 'db_connect.php';
include_once 'functions.php';
sec_session_start(); // Our custom secure way of starting a PHP session.
if (isset($_POST['email'], $_POST['p'])) {
$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$password = $_POST['p']; // The hashed password.
if (login($email, $password, $mysqli) == true) {
// Login success
header("Location: ../protected_page.php");
exit();
} else {
// Login failed
header('Location: ../index.php?error=1');
echo $active;
exit();
}
} else {
// The correct POST variables were not sent to this page.
header('Location: ../error.php?err=Could not process login');
exit();
}
When I try to echo the variable $active it returns nothing.
Any help is appreciated in advance.
Posting this as a community wiki; I don't want rep for it, nor should there be any made from it.
A: You did not follow that tutorial exactly as it was written.
http://www.wikihow.com/Create-a-Secure-Login-Script-in-PHP-and-MySQL
since it is obvious that that is where that code comes from; I know it all too well.
You modified some parts of the code and left some out also.
Go back to the tutorial, and follow it " to a T ". You may also have to clear out your present hashes and start over.
Make sure that the table creation was done exactly as shown. If you failed to make the right columns and their proper lengths, then that will fail "silently" on you.
Consult the comments I left under the question also.
I'm creating a login page where the user name and password are entered and then checked against the database to see if they match (I have posted on this previously but my code was completely incorrect so I had to start over) Upon clicking the submit button the user should be directed to the homepage (index.php) if the two values match up or an error message should appear stating "Invalid login. Please try again." Very simple basic stuff. Yet, I cannot get any variation to work.
Here is my code without the validation check. I believe this code is right but, if not, could someone please explain as to why. I am not asking anyone to write any code, just explain why it is not working properly.
<?php
function Password($UserName)
{
//database login
$dsn = 'mysql:host=XXX;dbname=XXX';
$username='*****';
$password='*****';
//variable for errors
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
//try to run code
try {
//object to open database
$db = new PDO($dsn,$username,$password, $options);
//check username against password
$SQL = $db->prepare("Select USER_PASSWORD FROM user WHERE USER_NAME = :USER_NAME");
$SQL->bindValue(':USER_NAME', $UserName);
$SQL->execute();
$username = $SQL->fetch();
if($username === false)
{
$Password = null;
}
else
{
$Password = $username['USER_PASSWORD'];
}
return $Password;
$SQL->closeCursor();
$db = null;
} catch(PDOException $e){
$error_message = $e->getMessage();
echo("<p>Database Error: $error_message</p>");
exit();
}
?>
Now the validation code. I've googled this and found several hundred ways to do so but this method most closely matches my coding style. It is incomplete and I would like some help as to how to finish it properly and then where to place it within the code above. My assumption is right after this comment: "//check username against password". Now I've seen this version twice and in one version the check is for txtUserName and the other is just username. I believe there should be else statements after each if statement to direct them to the index.php page. Also, the third if statement is the check to see if the password matches the username. No variation of this did I understand. They were far too complex.
function Login()
{
if(empty($_POST['txtUserName']))
{
$this->HandleError("UserName is empty!");
return false;
}
if(empty($_POST['txtPassword']))
{
$this->HandleError("Password is empty!");
return false;
}
$username = trim($_POST['txtUserName']);
$password = trim($_POST['txtPassword']);
if(!$this->($username,$password))
{
return false;
}
}
I know I am asking a lot here. But I am very new to PHP and am really trying hard to learn it. And there is way too much info out there and most of it is not for beginners. Any, and all, help would be greatly appreciated.
To begin with, let's assume that we have a PDO connection, just like you do already, for example with this function:
You can do something like:
// Usage: $db = connectToDataBase($dbHost, $dbName, $dbUsername, $dbPassword);
// Pre: $dbHost is the database hostname,
// $dbName is the name of the database itself,
// $dbUsername is the username to access the database,
// $dbPassword is the password for the user of the database.
// Post: $db is an PDO connection to the database, based on the input parameters.
function connectToDataBase($dbHost, $dbName, $dbUsername, $dbPassword)
{
try
{
return new PDO("mysql:host=$dbHost;dbname=$dbName;charset=UTF-8", $dbUsername, $dbPassword);
}
catch(Exception $PDOexception)
{
exit("<p>An error ocurred: Can't connect to database. </p><p>More preciesly: ". $PDOexception->getMessage(). "</p>");
}
}
So that you can have a database connection like this:
$host = 'localhost';
$user = 'root';
$dataBaseName = 'databaseName';
$pass = '';
$db = connectToDataBase($host, $databaseName, $user, $pass);
So far we have the same stuff as you.
Now, I assume that we're on a PHP page where the user submitted his username and password, to begin with: check if we really received the username and the password, with the ternary oprator:
// receive parameters to log in with.
$userName = isset($_POST['userName']) ? $_POST['userName'] : false;
$password = isset($_POST['password']) ? $_POST['password'] : false;
Now you can validate if those inputs were actually posted:
// Check if all required parameters are set and make sure
// that a user is not logged in already
if(isset($_SESSION['loggedIn']))
{
// You don't want an already logged in user to try to log in.
$alrLogged = "You're already logged in.";
$_SESSION['warningMessage'] = $alrLogged;
header("Location: ../index.php");
}
else if($userName && $password)
{
// Verify an user by the email address and password
// submitted to this page
verifyUser($userName, $password, $db);
}
else if($userName && (!($password)))
{
$noPass = "You didn't fill out your password.";
$_SESSION['warningMessage'] = $noPass;
header("Location: ../index.php");
}
else if((!$userName) && $password)
{
$noUserName = "You didn't fill out your user name.";
$_SESSION['warningMessage'] = $noUserName;
header("Location: ../index.php");
}
else if((!$userName) && (!($password)))
{
$neither = "You didn't fill out your user name nor did you fill out your password.";
$_SESSION['warningMessage'] = $neither;
header("Location: ../index.php");
}
else
{
$unknownError = "An unknown error occurred.". NL. "Try again or <a href='../sites/contact.php' title='Contact us' target='_blank'>contact us</a>.";
$_SESSION['warningMessage'] = $unknownError;
header("Location: ../index.php");
}
Now, let's assume that everything went well and you already have a database connection stored in the variable $db, then you can work with the function
verifyUser($userName, $password, $db);
Like already mentioned in the first else if statement:
// Usage: verifyUser($userName, $password, $db);
// Pre: $db has already been defined and is a reference
// to a PDO connection.
// $userName is of type string.
// $password is of type string.
// Post: $user exists and has been granted a session that declares
// the fact that he is logged in.
function verifyUser($userName, $password, $db)
{
$userExists = userExists($userName, $db); // Check if user exists with that username.
if(!($user))
{
// User not found.
// Create warning message.
$notFound= "User not found.";
$_SESSION['warningMessage'] = $notFound;
header("Location: ../index.php");
}
else
{
// The user exists, here you can use your smart function which receives
// the hash of the password of the user:
$passwordHash = Password($UserName);
// If you have PHPass, an awesome hashing library for PHP
// http://www.openwall.com/phpass/
// Then you can do this:
$passwordMatch = PHPhassMatch($passwordHash , $password);
// Or you can just create a basic functions which does the same;
// Receive 1 parameter which is a hashed password, one which is not hashed,
// so you hash the second one and check if the hashes match.
if($passwordMatch)
{
// The user exists and he entered the correct password.
$_SESSION['isLoggedIn'] = true;
header("Location: ../index.php");
// Whatever more you want to do.
}
else
{
// Password incorrect.
// Create warning message.
$wrongPass = "Username or password incorrect."; // Don't give to much info.
$_SESSION['warningMessage'] = $wrongPass;
header("Location: ../index.php");
}
}
}
And the function userExists($userName, $db) can be like:
function userExists($userName, $db)
{
$stmt = $db->prepare("SELECT * FROM users WHERE USER_NAME = :USER_NAME;");
$stmt->execute(array(":USER_NAME "=>$userName));
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if($result)
{
// User exists.
return true;
}
// User doesn't exist.
return false;
}
Where the function Password is like:
function Password($UserName)
{
$stmt = $db->prepare("Select USER_PASSWORD FROM user WHERE USER_NAME = :USER_NAME;");
$stmt->execute(array(":USER_NAME"=>UserName));
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if($result)
{
return $result['USER_PASSWORD'];
}
// No result.
return false;
}
Again, make sure you're not matching plain text passwords, or basic shai1, md5 encryptiones etc. I really recommend that you take a look at PHPass.
I hope I'm making myself clear.