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.
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.
On this project, I've used BCRYPT to make a hash off the password
On logging in, the user normally logging in without any errors or whatsoever, but when trying to change your password, your current password does not match with the password in the database (of course i'm using password_verify() to verify the two passwords)
Here is the snippet of my code:
$option = ['cost' => 12];
$password = password_hash($_currentpassword, PASSWORD_BCRYPT, $option);
$selectpasswordsql = "SELECT `password` FROM `auth` WHERE username=?";
$selectpasswordstmt = $conn->prepare($selectpasswordsql);
$selectpasswordstmt->execute(array($_SESSION['account']['username']));
$selectpasswordresults = $selectpasswordstmt->fetch(PDO::FETCH_ASSOC);
$databasepass = $selectpasswordresults['password'];
$databasepass = trim($databasepass);
if(password_verify($password,$databasepass)){
if(empty($passmsgs)){
$updatepasssql = "UPDATE `auth` SET
`password`=?
WHERE username=?
";
$updatepassstmt = $conn->prepare($updatepasssql);
$updatepassstmt->execute(array($password, $_SESSION['account']['username']));
if($updatepassstmt){
array_push($passmsgs, 'Successfully updating your password!');
} else {
array_push($passmsgs, 'There was a problem executing your command!');
}
}
} else {
array_push($passmsgs, 'Your current password is wrong!');
}
Trying this out will cause an error of not matching your current password with the password
Edit: Yes I am using VARCHAR with a maximum length of 255
Edit 2: Here is a link to the full copy of my codes.
You don't need to hash your $_currentpassword variable in the second line.
Just pass the variable to the password_verify function and the function itself will do the job.
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.
I am making a log in and registration form. Since I am dealing with passwords, I want to do it right so excuse the long lines of code. I have manage to do the registration form that will hash the password. But My problem is when logging in the password is not reading it and I am using only one mock account and one password. Do you think its the hashing? Please help
PHP Code(I have made a functions.php file that has the functions needed to do this log in it contains)
login function
function login($email, $password, $mysqli) {
// Using prepared Statements means that SQL injection is not possible.
if ($stmt = $mysqli->prepare("SELECT accountID, UserName, Password, salt FROM accounts WHERE email = ? LIMIT 1")) {
$stmt->bind_param('s', $email); // Bind "$email" to parameter.
$stmt->execute(); // Execute the prepared query.
$stmt->store_result();
$stmt->bind_result($user_id, $username, $db_password, $salt); // get variables from result.
$stmt->fetch();
$password = hash('sha512', $password.$salt); // hash the password with the unique 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 {
if($db_password == $password) { // Check if the password in the database matches the password the user submitted.
// Password is correct!
$user_browser = $_SERVER['HTTP_USER_AGENT']; // Get the user-agent string of the user.
$user_id = preg_replace("/[^0-9]+/", "", $user_id); // XSS protection as we might print this value
$_SESSION['user_id'] = $user_id;
$username = preg_replace("/[^a-zA-Z0-9_\-]+/", "", $username); // XSS protection as we might print this value
$_SESSION['username'] = $username;
$_SESSION['login_string'] = hash('sha512', $password.$user_browser);
// Login successful.
return true;
} else {
// Password is not correct
// We record this attempt in the database
$now = time();
$mysqli->query("INSERT INTO login_attempts (user_id, time) VALUES ('$user_id', '$now')");
return false;
}
}
} else {
// No user exists.
return false;
}
}
}
I have a checkbrute function that deals with forced logins
function checkbrute($user_id, $mysqli) {
// Get timestamp of current time
$now = time();
// All login attempts are counted from the past 2 hours.
$valid_attempts = $now - (2 * 60 * 60);
if ($stmt = $mysqli->prepare("SELECT time FROM login_attempts WHERE user_id = ? AND time > '$valid_attempts'")) {
$stmt->bind_param('i', $user_id);
// Execute the prepared query.
$stmt->execute();
$stmt->store_result();
// If there has been more than 5 failed logins
if($stmt->num_rows > 5) {
return true;
} else {
return false;
}
}
}
Finaly I have a login_check to check if all session variables are set
function login_check($mysqli) {
// Check if all session variables are set
if(isset($_SESSION['user_id'], $_SESSION['username'], $_SESSION['login_string'])) {
$user_id = $_SESSION['user_id'];
$login_string = $_SESSION['login_string'];
$username = $_SESSION['username'];
$user_browser = $_SERVER['HTTP_USER_AGENT']; // Get the user-agent string of the user.
if ($stmt = $mysqli->prepare("SELECT Password FROM accounts WHERE accountID = ? LIMIT 1")) {
$stmt->bind_param('i', $user_id); // Bind "$user_id" to parameter.
$stmt->execute(); // Execute the prepared query.
$stmt->store_result();
if($stmt->num_rows == 1) { // If the user exists
$stmt->bind_result($password); // get variables from result.
$stmt->fetch();
$login_check = hash('sha512', $password.$user_browser);
if($login_check == $login_string) {
// Logged In!!!!
return true;
} else {
// Not logged in
return false;
}
} else {
// Not logged in
return false;
}
} else {
// Not logged in
return false;
}
} else {
// Not logged in
return false;
}
}
I am running the login form via separate html file
<body>
<form action="process_login.php" method="post" name="login_form">
Email: <input type="text" name="email" value=""/>
Password: <input type="password" name="password" id="password" value="" />
<input type="button" value="Login" onclick="formhash(this.form, this.form.password);" />
</form>
</body>
</html>
and the Process_login.php
include 'db_connect.php';
include 'functions.php';
sec_session_start(); // Our custom secure way of starting a php session.
if(isset($_POST['email'], $_POST['password'])) {
$email = $_POST['email'];
$password = $_POST['password']; // The hashed password.
if(login($email, $password, $mysqli) == true) {
// Login success
echo 'Success: You have been logged in!';
} else {
// Login failed
echo 'Fail';
}
} else {
// The correct POST variables were not sent to this page.
echo 'Invalid Request';
}
Thanks
Absolute ZERO is right, you are using random salts and you are comparing password hashes as a whole with each other, they will never match. I think you should be using bcrypt. In that you can create random and unique salts, but they crypt() function in that only checks for the part that was hashed and not the salts.
This is a more sensible way of doing it and you won't have to create a separate column for storing salts. Also bcrypt is probably the safest method hashing method available today, and it is really slow, which would only be a problem for attackers and not your users.
I don't know a tutorial that shows how to use bcrypt, but I'm sure u'll find tons when u search on Google. However, here is a very good tutorial on a Log in and registration system that also uses bcrypt(): http://www.sunnytuts.com/article/login-and-registration-with-object-oriented-php-and-pdo
I think you'll find it really useful, but make sure you continue to Part 2 of that tutorial.
Good luck.
You've got the right idea with the security, but if you're generating a new random salt on every attempt when you're trying to compare with an existing record it should never match (it's random)*. You have to store the salt in the table along with the salted hashed password. The salt should be random for each user (as in random, not their user agent), but you'll need to use the salt from the creation of the account to hash the user's password input for comparison.
To make the fix:
You'll need to add a 'salt' column to your accounts table (or a relation table that you can relate to the accounts table).
Once that's added then you would need to adjust any of the functions where you're doing an insert into the database with the user's information. You'll need to add salt='$salt' to the SQL.
On the checks for input login_check() and login() you'll need to do the following:
Update the SQL for the query to check the username first:
"select count(*) from accounts where username = '$username'"
If the count is '1', then do this:
"select salt from accounts where username = '$username'"
Then you would do this:
$password_for_comparison = hash('sha512', $password.$salt_from_database);
Then you would do this:
"select count(*) from accounts where username='username' and password='$password_for_comparison'"
If this count is 1 then you have a valid user.
Those few things can be added into a new function and called in the other functions (so you're not maintaining two separate code sets).
*Note: There are cases where a collision may occur, but with your selection of sha512 this is such a random possibility it's unlikely without someone trying brute force the thing with something like hashcat on a box with 25 vid cards running opencl.
A simple Registration and login page for a website. I am trying to crypt the password + salting. That's what I have done but I am not sure if its correct.
This is what happens in the registration page:
$blowfish = '$2a$10$';
$salt = '8dF$d_3';
$hashedPass = crypt($password,$blowfish . $salt);
In the database, the password "RAYray99" is stored as "$2a$10$8dF$d_3$$$$$$$$$$$$$$.wxsfa7X.nkcGqldJ9fujdd8eY.H85uC"
On the login page, I am stuck on how to check the password entered with the password in the database.
This is the loging php script that verifies the user:
mysql_connect("$db_host", "$db_username", "$db_pass") or die(mysql_error());
//select the database or return error message
mysql_select_db("$db_name") or die("database does not exist");
$email = stripslashes($_POST['email']);
$email = strip_tags($email);
$email = mysql_real_escape_string($email);
$password = ereg_replace("[^A-Za-z0-9]", "", $_POST['password']); // filter everything but numbers and letters
$password = crypt($password);
$sql = mysql_query("SELECT * FROM members WHERE email='$email' AND password='$password' AND activateemail='1'");
$login_check = mysql_num_rows($sql);
if($login_check > 0){
while($row = mysql_fetch_array($sql)){
// Get member ID into a session variable
$id = $row["id"];
session_register('id');
$_SESSION['id'] = $id;
// Get member username into a session variable
$username = $row["username"];
session_register('username');
$_SESSION['username'] = $username;
// Update last_log_date field for this member now
mysql_query("UPDATE members SET lastlogin=now() WHERE id='$id'");
// Print success message here if all went well then exit the script
header("location: account.php");
exit();
} // close while
} else {
// Print login failure message to the user and link them back to your login page
print '<br /><br /><font color="#FF0000">ERROR TRY AGAIN </font><br />;
exit();
}
My question is how would I verify the login passsword entered on the login page with the 1 in the database.
Thank you,
Ray
To verify the password you should be hashing the entered one exactly the same way you did when you stored it in the first place.
I see some issues with what you have. First, the blowfish string for crypt() specifies, according to the PHP docs, that after your $2a$10$ should be a 22 character string from the alphabet of ./0-9A-Za-z. Your salt isn't following that, so the hash function is probably failing completely (though I'm not certain).
Also, your salt shouldn't be a constant in your app, it should be unique to each user and stored in the db along with their hashed password.
Finally, you shouldn't be doing those transformations to the password before hashing it. Break out a function that has the plaintext password as input, and outputs the hashed password. Use this function both when you store the password, and when you attempt to validate the login. That way you know for sure it should match the database.
I am using a mapper for my sql queries (doctrine2).
My passwords are saved with md5 + salt in my db
public function setPassword($password) {
$this->_password = self::encodePassword ( $password );
return $this;
}
/**
* This function encodes the password to md5 after adding a salt
* #param string $password
*/
public static function encodePassword($password) {
$salt = 'dsa7893ujlksdsagkz27392kjsjaldksju928ikljda27';
return md5 ( $password . $salt );
}
My login looks like this ...
/**
* Executes an authentication try
*
* #throws Zend_Auth_Adapter_Exception if the authentication failed
* #return Zend_Auth_Result
*/
public function authenticate()
{
//check if login is correct
try{
$user = $this->_em->getRepository('App_Model_User')
->findOneBy(
array(
'_email' => $this->_login,
'_password' => App_Model_User::encodePassword( $this->_password )
)
);
}catch( Exception $e ){
throw new Zend_Auth_Adapter_Exception( 'authentication failed', 0, $e );
}
.............
$password = $_POST['password'];
$str = "$2a$10$8dF$d_3$$$$$$$$$$$$$$.wxsfa7X.nkcGqldJ9fujdd8eY.H85uC";
if (preg_match('/^(.{7})(.{7})(.+?)$/', $str, $m))
{
$blowfish = $m[1];
$salt = $m[2];
$hashedPass = $m[3];
$validHashedPass = crypt($password,$blowfish.$salt);
if ($validHashedPass == $hashedPass)
{
good pass
}
else
{
wrong auth
}
}
I have a class built just for this case. It uses the sha1() hashing function and PDO prepared statements to prevent SQL injections. I'll try to find it and post it here, but if I don't, this is my advice to you:
Use a hashing function, not a crypting one.
Don't use mysql_* as it does not support prepared statements. Use PDO or at the very leat, mysqli.
As #Brad pointed out it'll be simpler (and to some extent safer) to hash the passwords instead of encrypting them.
MySQL supports both MD5 and SHA1 hashes anyhow.
One of the advantages of going this route is you can do either:
$pass = md5($salt . $pass);
$SQL = "SELECT * FROM users WHERE username = '$user' AND pass = '$pass';";
OR directly on the database
$SQL = "SELECT * FROM users WHERE username = '$user' AND pass = MD5('{$salt}{$pass}')";