I'm looking for a simple (or as simple as possible) yet secure method for hashing and salting a users password when submitting it to the database and then retrieving from the database. Having spent the past 3 hours researching, there are literally hundreds of different methods that each person will say is the best method.
I'm looking for a relatively simple method that will also keep users accounts secure. Obviously the term secure can be interpreted differently, but i just want it, at the very least, be very difficult for a would-be hacker (or whatever you'd call these saddo's) to gain access to a users account.
I appreciate that i should have at least tried a few things, but they all seem so convoluted and overly secure for my purpose.
I tried using password_hash() but it appears i'm running an earlier PHP version than 5.5. I understand there are issues with the code below, but it's simply a starting point for a person project i'm working on in order to better learn PHP.
Current registration form
$username = $_POST['username'];
$password = $_POST['password'];
try {
$result = $db->prepare("INSERT INTO
user_info
SET
username = :user,
pass = :pass
");
$result->bindParam(':user', $username);
$result->bindParam(':pass', $password);
$result->execute();
}
catch (Exception $e) {
echo "Could not create username";
}
if (isset($_POST['submit'])) {
foreach ($_POST as $field) {
if (empty($field)) {
$fail = true;
}
else {
$continue = false;
}
}
if ($field == $fail) {
echo "You must enter a username and/or password";
}
else {
echo "Your account has been successfully created.";
}
}
The login logic
$username = $_POST['username'];
$password = $_POST['password'];
try {
$result = $db->prepare("SELECT username, pass FROM user_info WHERE username = :user AND BINARY pass = :pass");
$result->bindParam(':user', $username);
$result->bindParam(':pass', $password);
$result->execute();
$rows = $result->fetch(PDO::FETCH_NUM);
}
catch (Exception $e) {
echo "Could not retrieve data from database";
exit();
}
if ($password = $rows) {
session_start();
$_SESSION['username'] = $_POST['username'];
$_SESSION['loggedin'] = true;
include("inc/redirect.php");
} else {
if (isset($_POST['login'])) {
echo "Username or password incorrect (passwords are case sensitive)";
}
}
Use sha1 function http://www.php.net/manual/en/function.sha1.php
It's really simple. Pass the password in input parameter then save it in the database.
When you want to check if password is correct you just have to compare the sha1(password) with the stored value.
Example :
$passwordEncrypted = sha1($password)
save $passwordEncrypted in your database
When the user want to login :
check this condition :
if (sha1($password) ==$passwordEncrypted )
Here is the complete code :
$username = $_POST['username'];
$password = $_POST['password'];
$passwordEncrypted = sha1($password)
try {
$result = $db->prepare("INSERT INTO
user_info
SET
username = :user,
pass = :pass
");
$result->bindParam(':user', $username);
$result->bindParam(':pass', $passwordEncrypted);
$result->execute();
}
catch (Exception $e) {
echo "Could not create username";
}
if (isset($_POST['submit'])) {
foreach ($_POST as $field) {
if (empty($field)) {
$fail = true;
}
else {
$continue = false;
}
}
if ($field == $fail) {
echo "You must enter a username and/or password";
}
else {
echo "Your account has been successfully created.";
}
}
With password_hash() you are on the right track. For PHP versions 5.3.7 - 5.5 you can use the compatibility pack, later when you switch to a newer PHP version, you can simply remove this php file from your project and the code will still run.
// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
Even for lower PHP versions than 5.3.7 you can use the compatibility pack. You only have to edit line 55 and change the algorithm from sprintf("$2y$%02d$", $cost); to sprintf("$2a$%02d$", $cost);. This is of course not optimal, but it is the best you can do for PHP between 5.3 and 5.3.7.
The problem with other algorithms like SHA* or MD5 is, that they are ways too fast. It is possible to calculate about 3 Giga SHA-1 per second with common hardware, that makes brute-forcing too easy. To test a whole english dictionary you would need only a fraction of a millisecond. That's why one should use a hash algorithm with a cost factor like BCrypt or PBKDF2, they allow to control the necessary time to calculate a single hash.
Look at this question:
https://stackoverflow.com/questions/3897434/password-security-sha1-sha256-or-sha512
You can use hash function
http://www.php.net/manual/en/function.hash.php
Use salt and sha256 encryption algorithm
<?php
// create user
$password = $_POST['password'];
$salt = mcrypt_create_iv(22, MCRYPT_DEV_URANDOM);
$pass = hash("sha256", $salt.$password.$salt);
// save salt and password hash into database
// to validate user
// 1 - get salt and password hash from database
// 2 - prepend and append salt to the posted password
// 3 - encrypt with the same algorithm
// 4 - compare with stored password hash.
Related
my login activity cannot read encrypted Password i tried without encrypted password and it works and im not sure if the error from php or activity itself of how to decryption password
im Using PASSWORD_BCRYPT
<?php
include "conn.php";
$Email = $_POST['Email'];
$Password = $_POST['Password'];
$sql_login = "SELECT * FROM users WHERE Email = :EMAIL and Password =:PASSWORD";
$stmt = $PDO->prepare($sql_login);
$stmt->bindParam(':EMAIL', $Email);
$stmt->bindParam(':PASSWORD', $Password);
$stmt->execute();
if ($stmt->rowCount() > 0) {
$returnApp = array('LOGIN' => 'SUCCESS');
echo json_encode($returnApp);
}else{
$returnApp = array( 'LOGIN' => 'FAILED');
echo json_encode($returnApp);
}
?>
To correctly use hashing of a password in PHP, use the password_hash and password_verify combination.
When a user signs up, you get his password, hash it and store it in the database:
$hash = password_hash($_POST['newpassword'], PASSWORD_DEFAULT);
// store $hash in database column "password"
When this user wants to login, you check against the hash:
// fetch hash from database, store it in $stored_hash
$logged_in = password_verify($_POST['password'], $stored_hash);
if ($logged_in === TRUE) {
echo "Welcome!";
} else {
echo "Username or password incorrect.";
}
Final notes:
Use PASSWORD_DEFAULT and make sure your database can store the result (also in the future). Hashing algorithms happen to get cracked once in a while.
You could use another provider like Google or Facebook to handle your authentication. This does have its drawbacks as well though.
I have question about valid hashing passwords:
login.php
$login = $_POST['login'];
$password = $_POST['password'];
$hash = password_hash($password, PASSWORD_DEFAULT);
if(!empty($login) && !empty($password) && password_verify(??){
I want to make secure login and I know that I have to verify the inputted password with existing hash (stored in database?). Also I know that bcrypt everytime makes new hash (because of different salt size etc).
The problem is that I don't know how to put this hash into database because I don't want to register new users, I have static list of users (exactly two: admin and user).
I tried manually copy/paste hash but it wouldn't work as I mentioned earlier that every time I run script new hash is created.
Can anyone help me how to put bcrypt hash in database (only once) so I can only check if inputted password is same as the one in database?
Do I need extra variables to store this hash?
EDIT:
login.php
<?php
session_start();
include("log.php");
include("pdo.php");
$login = $_POST['login'];
$password = $_POST['password'];
$adminHash = '$2y$10$lxPRtzzPDUZuPlodhU4QquP.IBrGpkjMNplpNgN9S1fEKd64tJ5vm';
$userHash = '$2y$10$Klt345wT66vA.4OAN5PEUeFqvhPQJ4Ua/A4Ylpc1ZcnJZv/hafgSu';
if(!empty($login) && !empty($password) && (password_verify($password, $adminHash) || password_verify($password, $userHash))){
$query = $pdo->prepare('SELECT * FROM xx WHERE login = ? AND admin = ?');
$query->execute(array( $login, 1));
$result = $query->fetchAll();
if(!empty($result)) {
$_SESSION['logged_admin'] = 1;
}
else {
$query->execute(array( $login, 0));
$result = $query->fetchAll();
if(!empty($result)) {
$_SESSION['logged_user'] = 1;
}
else {
$_SESSION['logged_error'] = 1;
}
}
}
else $_SESSION['logged_error'] = 1;
header("Location:index.php");
?>
it seems to be working but i dont know if it's best/safest solution.
With more passwords it will be too complicated i guess, still looking for best option!
What if i need more users? now every user have same hash and it's dangerous i get it, how to make it safe? generate hash for every user and make array or hashes?
You fetch first the one that has password_hash() from your database, and then compare it with password_verify($password, $storedpassword) like this : link
If I have this PHP script for a login system:
$user = $_POST['user_name'];
$pass = md5($_POST['user_pass']);
require_once("connection_file.php");
$sql = "SELECT * FROM login_table WHERE user_n = :us AND user_p = :password";
$stmt = $conn->prepare($sql);
$stmt->bindValue(':us', $user, PDO::PARAM_STR);
$stmt->bindValue(':password', $pass, PDO::PARAM_STR);
$stmt->execute();
$result = $stmt->fetchAll();
if($result)
{
//echo $msg = "user exist";
if(session_status() == PHP_SESSION_NONE)
{
session_start();
foreach($result as $row)
{
$hash = password_hash($row['user_pass'], PASSWORD_BCRYPT);
if(password_verify($row['user_pass'], $hash))
{
$_SESSION['userid'] = $row['user_id'];
$_SESSION['role'] = $row['user_role'];
header("Location: homepage.php");
}
}
}
}
else
{
$msg = "Wrong credentials";
header("Location: login_page.php");
}
And as you see I am already saving my password in database as MD5 and I am using $pass = md5($_POST['user_pass']); to verify if the text input by the user is equal to MD5 hash.
Now my question is should I use the password_hash and password_verify as I am using in this script ? Or using MD5 would be enough ?
And my second question is can I save passwords in database using the hash string result or it is okay to use the md5 one?
Yes, you should migrate to the new API and never use MD5 for this purpose again, immediately.
If you're not using password_hash()/password_verify() and want to migrate your code to a more secure method, seamlessly:
Add a column to your user accounts table, called legacy_password (or equivalent).
Calculate the bcrypt hash of the existing MD5 hashes and store them in the database (setting legacy_password to TRUE).
Modify your authentication code to handle the legacy flag.
When a user attempts to login, first check if the legacy_password flag is set. If it is, first pre-hash their password with MD5, then use this prehashed value in place of their password. Afterwards, recalculate the bcrypt hash and store the new hash in the database, disabling the legacy_password flag in the process. A very loose example in PHP 7+ follows:
/**
* This is example code. Please feel free to use it for reference but don't just copy/paste it.
*
* #param string $username Unsafe user-supplied data: The username
* #param string $password Unsafe user-supplied data: The password
* #return int The primary key for that user account
* #throws InvalidUserCredentialsException
*/
public function authenticate(string $username, string $password): int
{
// Database lookup
$stmt = $this->db->prepare("SELECT userid, passwordhash, legacy_password FROM user_accounts WHERE username = ?");
$stmt->execute([$username]);
$stored = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$stored) {
// No such user, throw an exception
throw new InvalidUserCredentialsException();
}
if ($stored['legacy_password']) {
// This is the legacy password upgrade code
if (password_verify(md5($password), $stored['passwordhash'])) {
$newhash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $this->db->prepare("UPDATE user_accounts SET passwordhash = ?, legacy_password = FALSE WHERE userid = ?");
$stmt->execute([$newhash, $stored['userid']]);
// Return the user ID (integer)
return $stored['userid'];
}
} elseif (password_verify($password, $stored['passwordhash'])) {
// This is the general purpose upgrade code e.g. if a future version of PHP upgrades to Argon2
if (password_needs_rehash($stored['passwordhash'], PASSWORD_DEFAULT)) {
$newhash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $this->db->prepare("UPDATE user_accounts SET passwordhash = ? WHERE userid = ?");
$stmt->execute([$newhash, $stored['userid']]);
}
// Return the user ID (integer)
return $stored['userid'];
}
// When all else fails, throw an exception
throw new InvalidUserCredentialsException();
}
Usage:
try {
$userid = $this->authenticate($username, $password);
// Update the session state
// Redirect to the post-authentication landing page
} catch (InvalidUserCredentialsException $e) {
// Log the failure
// Redirect to the login form
}
Proactively upgrading legacy hashes is a security win over an opportunistic strategy (rehashing when the user logs in, but leave the insecure hashes in the database for inactive users): With a proactive strategy, if your server gets compromised before everyone logs in again, their passwords are already using an acceptable algorithm (bcrypt, in the example code).
The above example code is also available in Bcrypt-SHA-384 flavor.
Also, this has nothing to do with encryption.
I have registered a new user and saved the username, password & salt in the DB using the following hashing method:
if(isset($_POST['register']))
{
$password = $_POST['password']
function sanitize($data)
{
$data=trim($data);
$data=htmlspecialchars($data);
$data=mysql_real_escape_string($data);
return $data;
}
$password = sanitize($password);
function createSalt()
{
$salt = bin2hex(mcrypt_create_iv(32,MYCRYPT_DEV_URANDOM));
$hash = hash("sha256", $salt);
$final = $salt.$hash;
return $final;
}
$hashedPassword = hash("sha256", $password);
$salt = createSalt();
$hashedPassword = hash("sha256", $hashedPassword.$salt);
$query = sprintf("INSERT INTO users(username, password, salt) VALUES('%s','%s','%s')",$username, $hashedPassword, $salt);
}
And Later while trying the login.php, I am entering the same password which I saved during registration and using the below code to check if the entered password is the same as the one in the DB
if(isset($_POST['login']]))
{
$password = $_POST['password']
function sanitize($data)
{
$data=trim($data);
$data=htmlspecialchars($data);
$data=mysql_real_escape_string($data);
return $data;
}
function validateUser()
{
session_regenerate_id (); //this is a security measure
$_SESSION['valid'] = 1;
$_SESSION['username'] = $username;
}
$password = sanitize($password);
$query = sprintf("SELECT * FROM users WHERE username = '%s'",$username);
$sql = mysql_query($query);
$count = mysql_num_rows($sql);
$row = mysql_fetch_array($sql);
if($count<1)
{
echo $count;
unset($_POST['login']);
header("location:login.php");
exit;
}
$hash = hash("sha256", $password);
$salt = $row['salt'];
$hash = hash("sha256",$hash.$salt);
echo $hash."<br />".$row['password']."<br /><br />";
if($hash != $row['password'])
{
unset($_POST['login']);
header("location:login.php");
exit;
}
else
{
validateUser();
unset($_POST['login']);
header("location:index.php");
exit;
}
}
These passwords are not getting matched.
Kindly let me know what's wrong in this code.
There is nothing wrong with your code.
the salt value stored in the database is truncated because the varchar value is low increase the varchar value of your salt column to 200-300 something and than try this.. it will run fine.
I facepalmed when I found out this was screwing the result..
Dins
Actually i didn't see why this should not work, the code you have shown, should produce the same value, maybe you could check, whether the salt you read from the database is really the same as you wrote to the database.
Nevertheless i would not engourage to go further on this route, there are quite a lot of problems here.
First of all, SHA-256 is not a good choice to hash passwords, instead use a slow key-derivation function like BCrypt.
You should not escape input data without need, and if you need to escape them, you should do it only for the specific target system (htmlspecialchars and mysql_real_escape_string make no sense if you are going to calculate a hash anyway).
To create a salt, you use the random source, that is good. Using a hash afterwards creating the salt, will in no way make the salt more random.
There is no need to have two separate fields for password and salt in the database. Php's crypt() function will create a hash value, that already contains the salt.
I would invite you to read this tutorial about hashing passwords, you will find a PHP example too, and i would recommend to use the phpass library.
I am fairly new to php security, and for my site, I was a sign up and login, and I want to add md5 their passwords, but I can't find anywhere which has a clear guide on what needs to be added to the sign up, and what needs to be added to the login files, and/or the database, as I say I am fairly new to php in terms of web security, so I am in need of some help, here's part of what I have on my sign up form:
$error = $user = $pass = "";
if (isset($_SESSION['user'])) destroySession();
if (isset($_POST['user']))
{
$user = sanitizeString($_POST['user']);
$pass = sanitizeString($_POST['pass']);
if ($user == "" || $pass == "")
{
$error = "Not all fields were entered<br /><br />";
}
else
{
$query = "SELECT * FROM members WHERE user='$user'";
if (mysql_num_rows(queryMysql($query)))
{
$error = "Username already taken<br /><br />";
}
else
{
$query = "INSERT INTO members VALUES('$user', '$pass')";
queryMysql($query);
die("<h4>Account created</h4>Please Log in.");
}
}
}
I just need an example or a good guide of what I need to do to get it working correctly.
I think you're looking to salt and then hash your passwords. Simply add a string of your choosing to the front (and if you wish, to the end) of your password before hashing it using MD5.
e.g.
$pass = 'mypassword';
$salt = 'S%gh3578'; //anything you want
$pepper = 'w890rrk'; //anything you want
$query = "INSERT INTO members VALUES('$user', md5('".$salt.$pass.$pepper."'))";
queryMysql($query);
This will store the password in the database using salted MD5 encryption that cannot be reversed using a lookup table of common passwords using unsalted MD5 encryption.
To check if a password is valid, you do something similar:
$passToCheck = 'something';
$correctMD5 = (retrieve hash from db)
if($salt.$passToCheck.$pepper == $correctMD5)
{
//valid login
} else {
//login failure
}
You can for example md5($pass) before you insert and when the user logs in, you md5 again and check that the values are the same. There is no way to de-md5, so you will usually check the md5 input against the md5 DB value.