RU beehive logo ITEC dept promo banner
ITEC 325
2019fall
ibarland

securing passwords
in a database

Securing the data stored in the database.

youtube (16m55s):

Suppose we store passwords or credit-cards in our database. What might someday happen (inferring from headlines)? Sadly, somebody might breach the db itself (aside from the website entirely) — either a hacker exploiting a DB security hole, or somebody accidentaly revealing the DB account password, or an inside employee stealing data.

Solution: Don't store the password in the database!
“But Barland, how the heck can we verify that they've typed in the correct password?”
We'll instead store a hashed form of the password. Then, when a user later types in the password on a form, we'll hash whatever they type, and see if the two hashes are equal.

php > echo hash('sha256', 'hello');
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
php > echo hash('sha256', 'helmo');
05c21a42da66f4e2d217e35b74ef9dc2d65c8d46f9aec0d6397a65dd3c766f21
     
(Yes, theoretically, there is tiny (but non-quite-zero!) chance that two different strings might happen to encrypt to the same value. In practice, I would wait to win the lottery nine times in a row1before I worry about two independently-chosen strings both happen to have hash-values that collide.)

Q1: If my form hashes the password client-side, and only sends the hash to the server to be verified, this is secure, right?
A1: No!
If an eavesdropper can overhear the hash being communicated, the server only sees/checks the hash to determine if a user knows the password, then such an eavesdropper would be able to fool the server!
So, even if using hashed passwords, be sure to use a secure (https) connection whenever sensitive information is transmitted.

Q2: Should you hash the password client-side, or server-side?
A2: server-side!
Although either approach protects against people guessing passwords, if your database were breached and an attacker glimpsed the hashed-passwords, you wouldn’t want them to be able to log in as another user. By having the password submitted to the server (over https of course), and having the server hash the string, it means that knowing the hash isn’t enough — an attacker would need to know some string which hashes to what was found in the database — and that’s exactly what secure hashes make difficult/impossible! (Of course, if an attacker has breached the database, you might have much bigger problems — did the attacker modify the database-contents, including changing other people’s passwords to hash('sha256','ha-ha, pwned!').)

Salting the hash

Of course, to help protect against brute-force dictionary attacks, you want to add a few extra "salt" characters to the password — say, 20 characters. And ideally, these should be different characters for each person (else, if an attacker found out the one salt you used, they could use that on every word in the dictionary and look for matches). … But it’s admittedly a bit of a hassle, to generate a salt for each user, and store that in the database as well as the hashed password.

Salting helps slow attacks on dictionary-based passwords, but is not a substitute for enforcing users to keep a good password à la xkcd.

Solution:

except, php on rucs...: Unfortunately, rucs’s old version of php is <5.5, so it does not support the functions below, so there you have to generate a salt, and store it, yourself.
The function password_hash does it for you: it returns a hashed password concatenated to a random salt. (See the format of the result.) Then later, when a user returns, you can call password-verify, giving it the previous string, and it returns a boolean value:
         php > var_dump( password_hash("kittee",PASSWORD_BCRYPT) );
         string(60) "$2y$10$hcqJaQl3bBxPcblU2QVm4eZ/Yc6pm4VnoE0AwS7cTb4Lag2ktuY8C"
         php > var_dump( password_verify("kittee",'$2y$10$hcqJaQl3bBxPcblU2QVm4eZ/Yc6pm4VnoE0AwS7cTb4Lag2ktuY8C') );
         bool(true)
         php > var_dump( password_verify("doge",  '$2y$10$hcqJaQl3bBxPcblU2QVm4eZ/Yc6pm4VnoE0AwS7cTb4Lag2ktuY8C') );
         bool(false)
         php > var_dump( password_hash("kittee",PASSWORD_BCRYPT) );
         string(60) "2y$10$Ije9mY4Mm9LwEFWSySGBmOGjzg1/Y6VLYLPnsmWVuWHzoYdiz5lyG"
       
(The bit in orange is the salt.) Note that calling password_hash twice with the same input gives different results, because it adds a different salt each time (which is what we want: two people with the same original password won't be given away by having the same hashed-password).
For more details, read the php docs: us.php.net/manual/en/faq.passwords.php#faq.password.storing-salts.

Urgent: IRL, use a password manager! Get your parents and family to do this, too! You might think you have a lot of different online accounts now that need migrating, but in 3yrs you'll only have more.

The reason is that the biggest threat to people is using the same password at multiple sites. Once one site (say, Target) is breached, it doesn't matter how good your password was — that info is sold on the black market to people who'll try the same username/password at every major bank in the world, and suddenly a 62-year-old near-retiree has their life savings wiped out, and has to keep working another decade… A password-manager not only uses passwords that a dictionary-attack can't guess, it makes sure that a password compromised at one site won't compromise your life savings. Using a password manager is much more important than avoiding a post-it with a password written on it (physical break-ins are very rare; some major site being breached is all too uncommon).


1 Why nine? Chances of winning the powerball lottery are about 1 in 292 million, which will approximate as 256Meg = 2^8*2^20 = 2^28. That's 28/4=7 hex digits. Sha256 returns strings with 64 > 7*9 hex digits.      

logo for creative commons by-attribution license
This page licensed CC-BY 4.0 Ian Barland
Page last generated
Please mail any suggestions
(incl. typos, broken links)
to ibarlandradford.edu
Rendered by Racket.