Cryptography
Recently I have been doing a lot of research into cryptography. It led me to discovering that, not only do we have salt, we also have pepper (yes I really only just found out about pepper).
Hashing passwords
This all started when I inherited a project in which passwords were not hashed. Even as a n00b I know that this is just silly.
I have a basic understanding of hashing, but what I knew was apparently outdated.
Original solution
My original hashes and salts were achieved by:
- Getting the user's password
- Combining it with a salt
- Hashing it using
MD5
- Storing both, the salt and the hash in a table
This worked well, but I am leaning towards using a better technique
New technique
The new way I wish to try is by using PHP's password_hash and password_verify.
Having created a quick test, I can see that I can return a true value with password_verify
on a given hash. The one thing that is puzzling me is where the salt comes in?
On the documentation I can see that there was an option to specify a salt in the options array but that is deprecated as of PHP 7.0.
My attempt
I have created some code (untested) just to demonstrate. See below code.
<?php
$pepper = "myPepper";
function register($username, $password)
{
// add the pepper
$password .= $pepper;
// hash the password
$hash = password_hash($password, PASSWORD_BCRYPT, ["cost" => 10]);
// insert into table
$query = $mysqli->prepare("INSERT INTO users(username, password) VALUES(?, ?)");
$query->bind_param("ss", $username, $hash);
// check the success
if ($query->execute())
return true;
else
return false;
}
function login($username, $password)
{
// create the query
$query = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
$query->bind_param("s", $username);
// check if successful
if ($query->execute())
{
// get password from the database
$query->store_result();
$query->bind_result($hash);
$query->fetch();
// verify the passwords are the same
if (password_verify($password . $pepper, $hash))
return true;
else
return false;
}
}
?>
register
function
Both these functions are very basic as I just wanted to show a point.
This function takes some parameters (in this case username
and password
and adds them to the database.
Now, I know how to add my pepper to the password as that is a simple concatenation, but the salt is randomly generated but never returned meaning I have no idea what it is.
login
function
Again, very basic in what it does.
Since I have not used password_verify
before I am not entirely sure I know the best approach to use to attain the user's password.
With my old login scripts they looked something like this:
function login($username, $password)
{
// create the query
$query = $mysqli->prepare("SELECT salt FROM users WHERE username = ? AND password = ?");
$pass = md5($password);
$query->bind_param("ss", $username, $password);
// check if successful
if ($query->execute())
{
$query->store_result(); // store result to gain access to num_rows
// verify if password and usernames match
if ($query->num_rows == 1)
return true;
else
return false;
}
}
Where I would just hash the password and pass it as a parameter into the SQL query.
With bcrypt I have to withdraw the password from table and then use it in another query. (At least that is what I believe I have to do currently).
Finally, the question part
Disclaimer
I apologise if I offended anyone with my lack of knowledge and / or my poor explanation of my knowledge.
Your help
I crave knowledge. So what I don't know, I want to know. (Obviously to an extent, I like to learn about computers and systems and programming etc).
Password hashing is essential these days and getting it right is very important and that is why I am writing this essay question.
Question(s)
- Is my understanding of bcrypt correct?
- Does it really not store a salt?
- In my basic example of
login
andregister
, have I implementedpassword_hash
andpassword_verify
?
Update 1
Following all the comments, I would just like to post this update.
As stated above I like to learn, so when I came across this function it started confusing me because I didn't know what was happening.
I am going to post some examples to try and effectively get my confusion across to everyone.
Let's take this script for example:
<?php
// everything below is hard coded for simplicity (would actually be extracted from a database)
$password = "myPassword"; // the password from the database
$salt = "mySalt"; // I have hard coded this for simplicity
$hash = md5($salt . $password);
// check login status
if (md5($salt . $_POST["password"]) == $hash)
return true;
else
return false;
?>
In this example I understand how the hashing works.
I am storing the password with a salt and hashing it. Then I am checking the posted password with the salt and hashing that. If the two match then I succeed with the login, otherwise the login has failed.
Now, let's take the following example.
<?php
// everything below is hard coded for simplicity (would actually be extracted from a database)
$password = "myPassword"; // the password from the database
$hash = password_hash($password, PASSWORD_BCRYPT, ["cost" => 10]); // salt is taken care of
// check login status
// again I am keeping this so simple (it might not work per se but I just want to learn about the functions)
if (password_verify($_POST["password"], $hash))
return true;
else
return false;
?>
Confusion
I start getting confused when I am running password_verify
.
I understand that the salt is taken care of by default in password_hash
, but in the first example I know the salt and so I can perform a hash with the posted password to check if they match.
Why, then, does password_verify
succeed in verifying the posted password without me having given it the salt?
Surely a salt is designed to make each password unique but somehow password_verify
will succeed. I have used var_dump
to dump the hash made from password_hash
and it changes upon refresh. This really is where the confusion comes from. If the hash of "test"
can change to a different hash each refresh, then how does password_verify
know the posted password is correct?
I know that I can write a function to verify the user's password. What I really want to know is how can how PHP's password_verify
manage to validate to true
every time, despite the fact that my hash will change with each refresh.
Note: I understand that I will be storing the password in a database so the refreshing won't be a problem, but I was doing so to try and understand the function.
Comments
@RiggsFolly I know that the salt is done for me. The question is "How does the verify function know the random salt created in the hash function in order to validate to true?"
@RiggsFolly I understand the salt option was deprecated. Had it not been (and I was able to pass it a salt), I think I would understand this function a lot more. The whole idea of the verify function successfully validating a password against a hash without knowing the salt is actually blowing my mind.
Perhaps I am just being stupid.
@Alex Howansky How is the salt returned? A string such as $hash = $2y$10$Vaj4ZonpRJjE6kmfQffvOOeIVW3ZV31JJYVY79GtZ3GtioZKtDwku
means nothing, yet somehow password_verify("test", $hash")
returns true
.
@Machavity Having read the link, I can see that the salt is at the start of the hash, but how can the salt be in the final hash? I apologise for my confusion, and apparent stupidness, but I simply wish to understand password hashing so that I am better prepared for future uses.
@Fred -ii- Those custom function were just simple for the example (I didn't want to link an entire page of code). That being said, my current usage is in a self defined Class called User
, where I have a private
variable called $conn
which stores the mysqli
connection. Then from that I use $this->conn->prepare("SELECT * FROM ...")
to access the database.
Is that bad for scoping? What is the preferred way to store the connection within a self defined Class?