Passwords#
Key Ideas#
password_hash()
password_verify()
hashing
salts
How PHP Passwords Work#
password_hash() is a PHP function that creates a password hash using a strong one-way hashing algorithm The following algorithms are currently supported:
PASSWORD_DEFAULT: Uses the bcrypt algorithm (default as of PHP 5.5.0). Note that this constant is designed to change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from using this identifier can change over time. Therefore, it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice).PASSWORD_BCRYPT: Uses the CRYPT_BLOWFISH algorithm to create the hash. This will produce a standard crypt() compatible hash using the “2y2y” identifier. The result will always be a 60 character string, or false on failure.PASSWORD_ARGON2I: Uses the Argon2i hashing algorithm to create the hash. This algorithm is only available if PHP has been compiled with Argon2 support.PASSWORD_ARGON2ID: Uses the Argon2id hashing algorithm to create the hash. This algorithm is only available if PHP has been compiled with Argon2 support.
Here’s an example of how to use password_hash():
$password = "password123";
$hash = password_hash($password, PASSWORD_DEFAULT);
This code will generate a hash for $password using the default algorithm (PASSWORD_DEFAULT). The resulting hash can then be stored in a database for later use.
To verify a password, you can use the password_verify() function:
$password = "password123";
$hash = password_hash($password, PASSWORD_DEFAULT);
if (password_verify($password, $hash)) {
echo "Password is valid!";
} else {
echo "Invalid password.";
}
This code will verify that $password matches the hash generated by password_hash(). If they match, it will output “Password is valid!”.
**Reference **
https://www.php.net/manual/en/function.password-hash.php
(AICHJIM)
How Password Salts Work#
Password salting is a technique used to protect passwords stored in databases. It involves adding a random string of characters, called a salt, to the password before it is hashed. This makes it much more difficult for attackers to crack the passwords, even if they have access to the database.
Here’s how password salting works:
Salt generation: A unique salt is generated for each password. This salt is typically a random string of characters that is at least 32 bytes long.
Salt concatenation: The salt is concatenated (joined) to the password. This creates a unique input for the hashing function.
Hashing: The combined salt and password are passed through a cryptographic hash function. This produces a unique hash value for each password.
Storage: The salt and hash value are stored together in the database. The original password is never stored.
When a user attempts to log in, the salt is retrieved from the database and concatenated to the password entered by the user. This combination is then hashed, and the resulting hash value is compared to the stored hash value. If the two hash values match, the user is authenticated.
Salting provides several benefits for password security:
Prevents Rainbow Table Attacks: Rainbow tables are precomputed tables of hash values for common passwords. Attackers can use these tables to quickly crack passwords that have not been salted. However, salting makes rainbow table attacks ineffective because the salt changes the hash value for each password.
Protects against Duplicate Passwords: If two users have the same password, their hash values will be the same without salting. This makes it easier for attackers to identify common passwords. However, salting ensures that even if two users have the same password, their hash values will be different.
Increases Password Complexity: Salting effectively increases the complexity of passwords, making them more difficult to crack. This is because the salt adds additional entropy to the password, making it more difficult to guess or brute-force.
In summary, password salting is an essential security measure that helps protect passwords from being cracked. It is a simple but effective technique that should be used by all applications that store passwords.
(AIBJIM)
One Way Hashing#
A one-way hashing algorithm, also known as a cryptographic hash function, is a mathematical function that takes an input of any length (a message or data) and produces a fixed-size output of random characters called a hash value or digest. The hash value is a unique representation of the input data, and it is practically impossible to reverse the process and obtain the original input from the hash value. This property makes one-way hashing algorithms crucial for data security and integrity verification.
Key characteristics of one-way hashing algorithms:
Deterministic: The same input always produces the same hash value.
Irreversibility: It is computationally infeasible to reverse the hash function and obtain the original input from the hash value.
Collision Resistance: It is extremely difficult to find two different inputs that produce the same hash value.
Efficiency: The hash function should be computationally efficient to calculate the hash value quickly.
Applications of one-way hashing algorithms:
Password Protection: Hashing is used to store passwords securely in databases. Instead of storing plain-text passwords, their hash values are stored, making it difficult for hackers to retrieve the original passwords even if they gain access to the database.
Data Integrity: Hash values are used to verify the integrity of data. By comparing the hash value of the original data with the hash value of the received data, one can ensure that the data has not been tampered with during transmission or storage.
Digital Signatures: Hash values are used in digital signatures to ensure the authenticity and non-repudiation of electronic documents.
File Verification: Hash values are used to verify the integrity of downloaded files, ensuring that the downloaded file is identical to the original file.
Blockchain Technology: Hashing is a fundamental component of blockchain technology, ensuring the immutability and security of the blockchain ledger.
Examples of widely used one-way hashing algorithms include MD5, SHA-1, SHA-256, and SHA-512.
(AIBJIM)
SQL Script#
The key thing to look at there is the UNIQUE attribute for the username. Setting this to UNIQUE will cause the database to automatically check for duplicates. If it finds one, you can always trap the error and code an appropriate response.
-- SETUP DATABASE FOR FRESH INSTALL
DROP DATABASE IF EXISTS wp_newuser_demo;
CREATE DATABASE IF NOT EXISTS wp_newuser_demo;
USE wp_newuser_demo;
-- ------------------------------------------
-- NOT IF YOU RUN THE CODE ABOVE,
-- THIS STEP IS NOT NECESSARY
-- THE CODE BELOW IF FOR REFFERENCE IF
-- YOU CHOOSE TO KEEP AN EXISTING VERSION
-- OF THE DATABASE
DROP TABLE IF EXISTS tbl_user;
-- ALTERNATIVE IF YOU HAVE A TABLE
-- BUT WISH TO REMOVE ALL THE DATA
-- UNCOMMENT THE FOLLOWING
-- TRUNCATE tbl_user;
-- ------------------------------------------
CREATE TABLE tbl_user
(firstname VARCHAR(50),
lastname VARCHAR(50),
username VARCHAR (50) UNIQUE,
password VARCHAR(255),
user_ID int(6) AUTO_INCREMENT PRIMARY KEY);
Database Connection#
Die() will cause the script to completely stop.
<?php
// dbconnect.php
//wp_newuser_demo
//tbl_user
// Turn off all error reporting
error_reporting(0);
try
{
// connection information
$host = "127.0.0.1";
$dbname = "wp_newuser_demo";
$user = "dev";
$pw = "";
// connect to database
$pdo = new PDO("mysql:host=$host;dbname=$dbname",$user,$pw);
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
//only for educational use to demonstration connection status
$dbstatus = "Good database connection";
}
catch(PDOException $e)
{
// get any error messages
$dbstatus = "Database connection failed<br>".
$e->getMessage();
// use in production
//die();
}
// start session variables
SESSION_START();
// for lecture only
echo($dbstatus);
echo("<br><hr><br>");
Start Page#
A simple page demonstrating new user and login options.
<!-- default.php -->
<html>
<head>
<title>Home</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="myStyle.css">
</head>
<body>
<h1>Password Demo</h1>
<a href="NewUserRegistration.php">New User Registration</a><br><br>
<a href="login.php">Login</a><br><br>
<a href="memberpage.php">Member's Only</a><br><br>
<a href="logout.php">Logout</a><br><br>
</body>
</html>
New User Registration#
<?php
// turn of error warnings - DO THIS LAST
error_reporting(0);
//NewUserRegistration.php
require("dbconnect.php");
// print_r($_POST);
echo("<br><br>");
function sanitize($value){
// strip of any excess white spaces on the ends
$value = trim($value);
// get rid of any html or php tags
$value = strip_tags($value);
// convert special characters
$value = htmlspecialchars($value,ENT_QUOTES,'UTF-8');
// return the value
return $value;
}
if(isset($_POST['username']))
{
try
{
$pwd = $_POST['password'];
//sql statement
$sql_stmt = "INSERT INTO tbl_user (firstname,lastname,username,password)
VALUES(:firstname,:lastname,:username,:password)";
//prepare
$sql_stmt = $pdo->prepare($sql_stmt);
//sanitize
$firstname = sanitize($_POST['firstname']);
$lastname = sanitize($_POST['lastname']);
$username = sanitize($_POST['username']);
//option to clean the password
// ******** PASSWORD *******
//hash the password
$password = password_hash($pwd, PASSWORD_DEFAULT);
// ***************************
//bind param way
// $sql_stmt->bindparam(":firstname",$firstname);
// $sql_stmt->bindparam(":lastname",$lastname);
// $sql_stmt->bindparam(":username",$username);
// $sql_stmt->bindparam(":password",$password);
// using array
$param_array = array(
':firstname' => $firstname,
':lastname'=>$lastname,
':username'=>$username,
':password'=>$password);
//execute
$sql_stmt->execute($param_array);
echo('<p>User was successfully entered</p>');
}
catch(PDOException $ee)
{
echo($ee->getMessage());
echo("<br><br>");
echo($ee->getCode());
if($ee->getCode() == 23000)
{
//echo("Please select different username");
$_SESSION['logerr_message'] = "Please select different username";
echo('<script>alert("Plese use different username");</script>');
header('location: NewUserRegistration.php');
}
}
}
else
{
echo('
<!DOCTYPE html>
<html>
<head>
<title>New User</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="myStyle.css">
</head>
<body>
<div class="div1">
<h1>New User Registration</h1>
<form method="POST" action="NewUserRegistration.php">
<table border="1">
<tr>
<td>First Name</td>
<td><input type="text" name="firstname" size="25"></td>
</tr>
<tr>
<td>Last Name</td>
<td><input type="text" name="lastname" size="25"></td>
</tr>
<tr>
<td>User Name</td>
<td><input type="text" name="username" size="25" required></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password" size="25" required></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name="submit"></td>
</tr>
</table>
</form>
<p><a href="default.php">Home</a></p>
</div>
</body>
</html>
');
}
Login Page#
On this page, note the **error_reporting ** function. Passing zero to it, will turn off error reporting.
<?php
//--------------------
//login.php
//https://www.php.net/manual/en/function.error-reporting.php
// Turn off all error reporting
error_reporting(0);
require('dbconnect.php');
if(isset($_POST['username']))
{
$sql_login = "SELECT username, password ".
"FROM tbl_user ".
"WHERE username = :username";
//prepare
$sql_login = $pdo->prepare("$sql_login");
//sanitize
$username = filter_var($_POST['username'], FILTER_SANITIZE_STRING);
//bind param
$sql_login->bindParam(":username",$username);
//execute
$sql_login->execute();
//get dataset / result
$result = $sql_login->fetch();
if($result['password']== null)
{
echo("<br>Bad Username / or Password</br>");
}
else
{
//for your information - remove for production
print_r($result['password']);
echo('<br><br');
//store the password
$hash_db = $result['password'];
//reverse the password process and compare
if(password_verify($_POST['password'], $hash_db))
{
echo('<br><br>Password is valid');
//This session variable will set as a flag to
// let users log onto "Member / private" pages
$_SESSION['LoginStatus'] = true;
//Goto the members page
header("location: memberpage.php");
}
else
{
//bad login
$_SESSION['LoginStatus'] = false;
// Note: you could set a session variable
// and go to a customized error page
echo('<br>Invalid Password<br>');
}
}
}
?>
<html>
<head>
<title>Login</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="myStyle.css">
</head>
<body>
<div class="div1">
<form method="POST" action="Login.php">
<table border= "1">
<tr>
<td colspan="2">Login</td>
</tr>
<td>Username</td>
<td><input type="text" name="username" size ="25" value="" require</td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password" size ="25" value="" require</td>
</tr>
<tr>
<td></td>
<td><input type="submit" name="submit" value="Enter"</td>
</tr>
</table>
</form>
<a href="default.php">Home</a>
</div>
</body>
</html>
Logout#
<?php
//logout.php
//Start Session
session_start();
// unset deletes the specified session variable
unset($_SESSION['LoginStatus']);
//redirect too the home page
header('location: default.php');
Member Page#
<?php
// memberpage.php
//access the session variables
session_start();
print_r($_SESSION);
//*** Make sure to do this step
//*** It makes checks to see if the status flag is set to false to
//*** prevent unauthorised access to the page.
if(!isset($_SESSION['LoginStatus']) || $_SESSION['LoginStatus']== false)
{
header('Location:default.php');
}
?>
<html>
<head>
<title>Members</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="myStyle.css">
</head>
<body>
<div class="div1">
<h1>Members Only</h1>
<p>You are on the members only page.</p>
<p><a href="default.php">home</a> <a href="logout.php">Log Out</a></p>
</div>
</body>
</html>