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.
Related
So here is the deal, I have been following tutorials all day trying to resolve this issue I am having.
So far my webpage shows "Username invalid" , but I have confirmed in the inspector in chrome that it is infact passing the correct username and password to my login script (below) am I doing anything wrong?!
<?php
session_start();
// Change this to your connection info.
$DATABASE_HOST = 'db_ip';
$DATABASE_USER = 'db_user';
$DATABASE_PASS = 'db_pass';
$DATABASE_NAME = 'db_name';
// 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 = '$username'")) {
// Bind parameters (s = string, i = int, b = blob, etc), in our case the username is a string so we use "s"
$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 ($_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;
echo 'Welcome ' . $_SESSION['name'] . '!';
} else {
echo 'Incorrect password!';
}
} else {
echo 'Incorrect username!';
}
$stmt->close();
}
?>
I was able to resolve my issue with the following:
where I was defining $stmt I was using $username when it had not been defined yet, I changed this line
$con->prepare("SELECT id, password FROM `accounts` WHERE username = '$username'"))
to
$con->prepare('SELECT id, password FROM `accounts` WHERE username = ?'))
and added this above my execute statement:
$stmt->bind_result($id, $password);
I'm attempted to create a login authentication system using PHP. So far I've managed to query the DB to check if a username/password given by the user matches any rows in the DB. However I have a column in the DB named "isadmin" which stores a boolean value. I want to implement a check if true/false. Depending on the result depends on which php file is loaded (included).
EDIT: I have two php files, both containing the same HTML displaying the index page of a website. However, one php file is for regular users, the other is for admin users which will contain added features. When a user enters their username and password, I want a check for the user level of that login, Once the check is done it should show the appropriate php page.
$stmt = $pdo->prepare('SELECT * FROM Reg_User WHERE username = :username AND password = :password');
$details = [
'username' => $_POST['username'],
'password' => sha1($_POST['password'])
];
unset($_POST['submit']);
$stmt->execute($details);
if ($stmt->rowCount() > 0) {
$user = $stmt->fetch();
$_SESSION['loggedin'] = $user['user_id'];
echo 'Logged in as ' . $_POST['username'];
include 'index.php';
}
else {
echo 'Sorry, your username and password could not be found Please <a href="login.html">try again
or register!</a>';
}
A simple if/else statement will do it.
if ($user["isadmin"]) {
echo "Logged in as an admin.";
#you can include your related php page here.
} else {
echo "Logged in as an user.";
#you can include your related php page here.
}
There's no sanitizing of user input in your code, this is a must in a login system, try this after your login form.
info: I don't use PDO, $con is the MYSQLI connection.
<?php
// Handle log in
if (isset($_POST['login'])) {
$username = $_POST['username'];
$password = $_POST['password'];
// Sanitize username input
$username = strip_tags($username);
$username = trim($username);
$username = mysqli_real_escape_string($con, $username);
$username = urldecode($username);
// Sanitize password input
$password = strip_tags($password);
$password = trim($password);
$password = mysqli_real_escape_string($con, $password);
$password = urldecode($password);
}
?>
Your site should be set to https only, if it is ignore this link: htaccess redirect to https://www and you should be providing either a secure session cookie or a secure persistent cookie for users who are able to log in successfully. The code underneath this paragraph should be at the very top of your page before any html. This example is for time related persistent https secure cookie set to 1 day after which it will expire. You could use a session cookie but I find this annoys people if they frequent your site quite often, they don't want to have to log in again the same day if they close and reopen a browser or tab.
<?php
// All this code goes right at the top of your page before anything else!
function addcookie() {
global $condition;
if ($condition == "green") {
global $nameofcookie;
setrawcookie('loggedin', $nameofcookie, strtotime('+1 day'), '/', '', isset($_SERVER["HTTPS"]), true);
echo "<script>window.location.replace('https://example.com/mypage');</script>";
}
}
?>
The above code is will set a secure cookie using a function because you only want it firing after a successful login. The name of the cookie really should be random and unique, something based on microtime would work well. Make sure it's not anything important which could identify the user!IMPORTANT: the name of the cookie for reference should be created at the time of account creation and added to the users table so you can identify users and represent their login details.
Standard security measures should also include a separate table of the ip, time, date and username of who logged in. If your site is busy the table will fill quickly so you could set a cron job to clean old records to keep the size down, in that case you will need to add a column for datetime to identify the age of records.
Handling the login...
<?php
$condition = "red";
if (isset($_POST['login'])) {
$select_login = "select * from Reg_User where username='$username' and password='$password'";
$connect_login = mysqli_query($con, $select_login);
$rows_login = mysqli_num_rows($connect_login);
if ($rows_login == 0) {
// code here to handle failed logins, I would record them and use a 3 strike method
}
// Handle successful logins, add cookie
else {
while ($row_login=mysqli_fetch_array($connect_login)) {
// Retrieve cookie name here from table
$nameofcookie=$row_login['cookie'];
$condition = "green"; // This allows you to add the cookie
addcookie();
}
}
}
?>
Retrieving the cookie to authenticate users...
<?php
if (isset($_COOKIE['loggedin'])) {
$cookie = $_COOKIE['loggedin'];
$select_authenticated_user = "select * from Reg_User where cookie='$cookie'";
$connect_authenticated_user = mysqli_query($con, $select_authenticated_user);
while ($row_authenticated_user=mysqli_fetch_array($connect_authenticated_user)) {
// Retrieve values here from table
$logged_in_user=$row_authenticated_user['username'];
$logged_in_admin=$row_authenticated_user['isadmin'];
// Resolve admin status
if ($logged_in_admin == TRUE) {
$type = "admin";
} else {
$type = "member";
}
}
// Echo statement for logged in user with admin or not status, you could change the echo to a variable name if you want to use this in a specific place on your page.
echo "Welcome $logged_in_user<br/>
Type: $type
";
}
?>
Here's a link for obtaining IP's: How to get the client IP address in PHP
I'm creating a login module. The admin and the users use the same page for login. Based on the credentials the page directs accordingly to admin and user pages. After the admin logs in, the admin adds the users and assigns them a password. With this password the users login. My project works till here. The problem is , I have to write a script to force the user to change the password when they login for the first time.
For this, I have created a login table which consists of username,password,fname.
I'm a beginner in php. Hence stuck.
Please help me.
Login.php
{
$_SESSION['login'] = "OK";
$_SESSION['username'] = $user;
$_SESSION['password'] = $pass;
header('Location: adminpage.php');
}
else
{
$_SESSION['login'] = "";
header('Location: wrong.html');
}
adminpage.php
if($_SESSION['username'] == 'admin')
{
echo "<p align=right><a href='login.php'>Log out</a></p>";
echo "<p><a href='create_user.php'>Create a new user</a></p>";
echo "<p><a href='reports.php'>Reports</a></p>";
}
elseif
{
header('Location: userpage.php');
}
In your login table add a new column for exemple.
status TINYINT(1) NOT NULL DEFAULT -1.
Set it to any default value (i've suggested -1) and when user changes their password after first login, change it to 1. Check this value for login request of your user. Later you can also use this column to block user (status = 0)
I had the same question and solved it the following way.
Inside my part of the script that verifies the hashed password against the entered password I put in this part of code in an if statement:
This is the complete code:
// Prepare a select statement
$sql = "SELECT id, username, password, admin, status FROM loginusers WHERE username = ?";
if($stmt = $mysqli->prepare($sql)){
// Bind variables to the prepared statement as parameters
$stmt->bind_param("s", $param_username);
// Set parameters
$param_username = $username;
// Attempt to execute the prepared statement
if($stmt->execute()){
// Store result
$stmt->store_result();
// Check if username exists, if yes then verify password
if($stmt->num_rows == 1){
// Bind result variables
$stmt->bind_result($id, $username, $hashed_password, $admin, $status);
if($stmt->fetch()){
if(password_verify($password, $hashed_password)){
// Password is correct, so start a new session
session_destroy();
session_start();
// Store data in session variables and cookies
$_SESSION["loggedin"] = true;
$_SESSION["id"] = $id;
$_SESSION["username"] = $username;
$_SESSION["admin"] = $admin;
setcookie("admin", $admin, time()+ 86400 * 30,"/");
setcookie("username", $username, time()+ 86400 * 30,"/");
// Redirect user to forced password change page
if($status == '0'){
// Redirect to first password change page
header("location: /login/firstpassword.php");
} else {
header("location: /index.php");
} else {
// Display an error message if password is not valid
$password_err = "Wrong password.";
}
} else {
$password_err = "Error with database connection!";
}
} else{
// Display an error message if username doesn't exist
$username_err = "No account with this username.";
}
} else{
echo "Oops, something is wrong. Contact the website admin.";
}
// Close statement
$stmt->close();
}
This is the part that forces the user to a password change page:
// Redirect user to forced password change page
if($status == '0'){
// Redirect to first password change page
header("location: /login/firstpassword.php");
} else {
header("location: /index.php");
Small addition to my earlier answer, you have to set the 'status' to anything other then 0 to make the user skip the forced password change page on every following login.
I did this on the 'firstpassword.php' page by setting the 'status' to 1 with another SQL INSERT query. So the user only get's the password change page once.
Hope this helps!
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.
Can you please help me to figure out how shall I use this if statement to show different content to different type of users.
this is the code that I have already found on another question:
if($_SESSION['usertype'] == 2){ //do stuff here} if ($_SESSION['usertype']) == 1) { //do stuff here }
I want to use this on a page where only members can view the page, and depending on the usertype, it should show different content.
But I'm not able to send the usertype in the login page when a user logs in, this is the code used there (login.php):
<?php
// First we execute our common code to connection to the database and start the session
require("common.php");
// This variable will be used to re-display the user's username to them in the
// login form if they fail to enter the correct password. It is initialized here
// to an empty value, which will be shown if the user has not submitted the form.
$submitted_username = '';
// This if statement checks to determine whether the login form has been submitted
// If it has, then the login code is run, otherwise the form is displayed
if(!empty($_POST))
{
// This query retreives the user's information from the database using
// their username.
$query = "
SELECT
id,
username,
password,
salt,
email
usertype
FROM users
WHERE
username = :username
";
// The parameter values
$query_params = array(
':username' => $_POST['username']
);
try
{
// Execute the query against the database
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// This variable tells us whether the user has successfully logged in or not.
// We initialize it to false, assuming they have not.
// If we determine that they have entered the right details, then we switch it to true.
$login_ok = false;
// Retrieve the user data from the database. If $row is false, then the username
// they entered is not registered.
$row = $stmt->fetch();
if($row)
{
// Using the password submitted by the user and the salt stored in the database,
// we now check to see whether the passwords match by hashing the submitted password
// and comparing it to the hashed version already stored in the database.
$check_password = hash('sha256', $_POST['password'] . $row['salt']);
for($round = 0; $round < 65536; $round++)
{
$check_password = hash('sha256', $check_password . $row['salt']);
}
if($check_password === $row['password'])
{
// If they do, then we flip this to true
$login_ok = true;
}
}
// If the user logged in successfully, then we send them to the private members-only page
// Otherwise, we display a login failed message and show the login form again
if($login_ok)
{
// Here I am preparing to store the $row array into the $_SESSION by
// removing the salt and password values from it. Although $_SESSION is
// stored on the server-side, there is no reason to store sensitive values
// in it unless you have to. Thus, it is best practice to remove these
// sensitive values first.
unset($row['salt']);
unset($row['password']);
// This stores the user's data into the session at the index 'user'.
// We will check this index on the private members-only page to determine whether
// or not the user is logged in. We can also use it to retrieve
// the user's details.
$_SESSION['user'] = $row;
$_SESSION['usertype'] = $row;
// Redirect the user to the private members-only page.
header("Location: dashboard.php");
die("Redirecting to: dashboard.php");
}
else
{
// Tell the user they failed
print("Login Failed.");
// Show them their username again so all they have to do is enter a new
// password. The use of htmlentities prevents XSS attacks. You should
// always use htmlentities on user submitted values before displaying them
// to any users (including the user that submitted them). For more information:
// http://en.wikipedia.org/wiki/XSS_attack
$submitted_username = htmlentities($_POST['username'], ENT_QUOTES, 'UTF-8');
}
}
?>
What changes do I need to make in this code?
I am quite new to all this, any help is appreciated.
You need to edit the last bit of your if($login_ok) section to set the $_SESSION variables correctly:
if($login_ok)
{
...
$_SESSION['user'] = $row['username'];
$_SESSION['usertype'] = $row['usertype'];
...
}
From what I can see in your code, if the rest of it works correctly then the dashboard.php page should be able to access it like this:
<?php
require("common.php");
if($_SESSION['usertype'] == 2) {
//do stuff here
} elseif($_SESSION['usertype']) == 1) {
//do stuff here
}
?>