How to securely generate SSHA256 or SSHA512 hashes in PHP? - php

I am working on a web administration module for mailservers (it's open source if you'd like to take a look).
For that, I need to be able to generate hashed password that is readable by Dovecot. As described on their wiki, their recommended password hashing scheme is SSHA256 (the extra S is for salted).
It's also explained that this could be reasonably simple to implement with something like this PHP code:
$salt = 'generate_a_salt_somehow';
$hash = hash('sha256', $password . $salt);
However, from what I've read about cryptography, that is a rather naíve way to generate salted hashes, but if you're doing it wrong when typing A-E-S in your source code, I figured the same could be true in this case.
So if you have insight into cryptography, I'd love to hear about the most secure way to do this, be it mcrypt, mhash or whatever.

Password generation function in PHP:
$hash = "{SSHA256}".base64_encode(hash('sha256', $password.$salt, true).$salt);
Necessarily "true" - the third parameter...

The Dovecot wiki page you linked to explains what the exact hash format Dovecot uses is. You don't have any choice in the matter -- Dovecot has its expectations on what the hash will look like, so you have to play by its rules:
For most of the salted password schemes (SMD5, SSHA*) the salt is stored after the password hash and its length can vary. When hashing the password, append the salt after the plaintext password, e.g.: SSHA256(pass, salt) = SHA256(pass + salt) + salt.
So an appropriate password generation function in PHP might look like:
$hash = "{SSHA256}" . base64_encode(hash('sha256', $password . $salt) . $salt);

Related

Bycript/Blowfish and Salts with existing auth system

I'm trying to transition to Blowfish for an authentication system. Bear with me, I'm not a cryptographer and my understanding of Blowfish is not quite there yet.
The current setup uses sha1 and salts. The salts are generated for each user and stored in the database. It boils down to this:
$salt = $this->getSalt($username);
$hash = sha1($password . $salt);
if ($hash == $hashInDB)
{
// user is authenticated, set session id etc ...
}
The getSalt() method gets the salt stored in the database for the specified user.
Now if I understand everything correctly, with crypt I should be doing:
$salt = '$2a$07$' . $this->getSalt($username) . '$';
$hash = crypt($password, $salt);
if ($hash == crypt($password, $saltInDB))
{
// The user is authenticated, set session id etc..
}
To clarify, for the second example the $saltInDB variable, is a value like `'$2a$07$arandomsaltcreatedatregistration$'.
Am I doing it right?
Your example is almost correct.
When you create a hash with the crypt() function, you will see that the used parameters (algorithm, cost and salt) are part of the generated hash (the begin of the hash):
$2a$07$LCzy1mE0b9lS8Uyx9HEeUgHm8zH1iDDZ5...
That means, you can replace $saltInDB with $hashInDB, the crypt() function will extract the needed parameters automatically from $hashInDB. This also answers your question about storing the salt, just store the hash value in the database, the salt is included there. And yes you should generate a new salt for each password.
The '$' after the salt is not needed.
More information about how to generate a bcrypt-hash you can find here, if you are looking for a well established library, i can recommend phpass.

Login System Hash Salt

I am rewriting a PHP Login system and I just faced this
function createSalt()
{
$string = md5(uniqid(rand(), true));
return substr($string, 0, 3);
}
$salt = createSalt();
$hash = hash('sha256', $salt . $hash);
Actualy I never worked with salting before, I searched a bit and found it useful.
But my answer is, Is this a good way to work with salt?
Wouldn't $string = sha1(uniqid(mt_rand(), true)) be better?
And what about returning only 3 characters of the hash? I really don't get it.
What you think?
Consider using PHP's crypt() instead of reinventing the wheel. It is specifically designed for password hashing, and offers hash algorithms suited for that purpose.
I prefer sha1 or sha256, md5 is super-outdated, the sha-functions are way better. But this is my opinion, choose what you want.
What is really important in this case is the salt. A salt is always stored in plaintext together with the hash and is used to improve the length of a password (if you want to hash the password, might be something else of course) to prevent attacks based on rainbow/lookup tables. This is no protection against cracking the password by using bruteforce (which works quite well against md5, so use sha256 which is harder to crack).
Therefore it is totally unimportant if you use 32 random chars for the hash, or something like md5(mt_rand()) - important is the length. I would use something like
$hash = md5(mt_rand()) . md5(mt_rand());
md5() results in 32 bytes string, based on a random number (mt_rand() is better then uniqueid()). With this simpel line you get a very "strong" hash which should secure every password against rainbow tables.
I don't agree with the given function createSalt() - mainly for the same reasons as you.
My approach to this would be
define(SALT_LENGTH,32);
function createSalt()
{
$string='';
for ($i=0;$i<SALT_LENGTH;$i++) $string.=chr(rand(0,255));
return $string;
}
A good salt is a random byte sequence - no MD5 or SHA1 makes any sense, as there is nothing to hash!
I no master PHP programmer but I've been working on the same hash script for a login system.
Im storing it on GitHub - https://github.com/revitalagency/PHP5-Salt-Super-Admin
I created my hash using...
hash_hmac('sha256', $_POST['pass'], GLOBAL_SALT);
GLOBAL_SALT is defined in a config file not in the DB for extra protection.

Creating salt with Portable PHP password hashing framework

I'd like to use Portable PHP password hashing framework to hash passwords. But I find that its demos don't use salt for hashing a password. But it use a dummy salt for checking password which I find it strange and I don't understand this idea at all,
$dummy_salt = '$2a$08$1234567890123456789012';
if (isset($dummy_salt) && strlen($hash) < 20)
$hash = $dummy_salt;
I wonder, if I want to use convention method which I can generate the unique salt and store it for each user in my database, how can I use Portable PHP password hashing framework to generate salts?
This is the function I use to hash passwords but I have been told that sha512 has the same issue as sha1, wise to trust the expert like Portable PHP password hashing framework,
function hash_sha512($phrase,&$salt = null)
{
//$pepper = '!##$%^&*()_+=-{}][;";/?<>.,';
if ($salt == '')
{
$salt = substr(hash('sha512',uniqid(rand(), true).PEPPER_KEY.microtime()), 0, SALT_LENGTH);
}
else
{
$salt = substr($salt, 0, SALT_LENGTH);
}
return hash('sha512',$salt.PEPPER_KEY.$phrase);
}
Let me know if you have any idea. Thanks.
From the phpass article linked to from that page:
Besides the actual hashing, phpass
transparently generates random salts
when a new password or passphrase is
hashed, and it encodes the hash type,
the salt, and the password stretching
iteration count into the "hash
encoding string" that it returns. When
phpass authenticates a password or
passphrase against a stored hash, it
similarly transparently extracts and
uses the hash type identifier, the
salt, and the iteration count out of
the "hash encoding string". Thus, you
do not need to bother with salting and
stretching on your own - phpass takes
care of these for you.
...so it's not surprising that the examples don't use salting! You may be over-seasoning your code.
The code sample with the dummy salt is an example of how to prevent a timing attack by making sure that whether a user exists or not, the validation of the user takes the same amount of time, effectively by doing a dummy authentication for non-existent users. It needs a dummy salt because if the user doesn't exist, it won't have a stored salt to use.

Encrypt password before storing in database?

I have a password being passed from my iPhone app to the database via a php script, user.php.
The variable $pass is populated by the following:
$pass = str_replace("'", "", $_REQUEST['pass']);
How can I encrypt this before it's inserted into my database? I've read a little about the different techniques, but looking for the best way to manage this.
Thanks to everyone.
While the answer below is technically still correct, php has new recommendations with regards to the hashing algorithms to use. Their recommendation, as of php >= 5.5.0, is to use the password_hash and password_verify functions to hash and verify hashed passwords . As an added benefit, these functions automatically include an individualized salt as part of the returned hash, so you don't need to worry about that explicitly.
If you don't care about retrieving the actual password's value (from the database encrypted value), you can run a one-way hash algorithm on it (such as sha1). This function will return a specific length string (hash) which cannot be used to find the original string (theoretically). It is possible that two different strings could create the same hash (called a collision) but this shouldn't be a problem with passwords.
Example:
$pass = sha1($_REQUEST['pass']);
One thing, to make it a little more secure is to add a salt to the hash and run the hash function again. This makes it more difficult to generate a password hash maliciously since the salt value is handled server-side only.
Example:
$pass = sha1(sha1($_REQUEST['pass']).sha1("mySalt#$#(%"));
Use php's crypt library. Md5 is not encryption, it is hashing.
Also, salt your passwords. Why?
This answer
Another good answer
First, you should create a random user salt. Then you should store that and the password hash in the database.
$salt = md5(unique_id().mt_rand().microtime());
$pass = sha1($salt.$_REQUEST['pass']);
and save the $salt and $pass in the database. Then when they go to login you look up their row and check the hash:
$user = query('SELECT * FROM `user` WHERE username = ?', array($_REQUEST['username']));
if($user)
{
// If the password they give maches
if($user->pass === sha1($user->salt. $_REQUEST['pass']))
{
// login
}
else
{
// bad password
}
}
else
{
// user not found
}
Creating a user salt for each account insures rainbow tables are useless and anyone that broken into your server would have to brute-force each password.
Use crypt with some salt. Such as
$user = strip_tags(substr($_REQUEST['user'],0,32));
$plain_pw = strip_tags(substr($_REQUEST['pass'],0,32));
$password = crypt(md5($plain_pw),md5($user));
as on http://www.ibm.com/developerworks/opensource/library/os-php-encrypt/
Most basic: Hash it with MD5 or SHA1
$newpass = md5($_REQUEST['pass']);
or
$newpass = sha1($_REQUEST['pass']);
Recently I started storing the username hashed as well, so login attempts are secure using only hashed data for comparisons.
You can "salt" the hashes with extra data so if they are compromised, it's value cannot be found (try googling some simple hashed words).. i.e. use a site-wide string just to alter the standard hash like md5("mySiteSalt!!" . $_REQUEST['pass']); or something more advanced.
You should use SHA1 to hash your passwords for storage in the database. It's the simplest, yet most effective way to store passwords:
$password = sha1($password);
It's also exceptionally safe. Though the integrity of it is beginning to creep, it's rather easy to upgrade this function to SHA-256 (which is incredibly secure).
To find out why md5, sha1 and their speedy friends might not be a good idea, you should read the post Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes by Thomas Ptacek. The gist:
Finally, we learned that if we want to
store passwords securely we have three
reasonable options: PHK’s MD5 scheme,
Provos-Maziere’s Bcrypt scheme, and
SRP. We learned that the correct
choice is Bcrypt.
Note: it's PHK, not php.

How can I store my users' passwords safely?

How much more safe is this than plain MD5? I've just started looking into password security. I'm pretty new to PHP.
$salt = 'csdnfgksdgojnmfnb';
$password = md5($salt.$_POST['password']);
$result = mysql_query("SELECT id FROM users
WHERE username = '".mysql_real_escape_string($_POST['username'])."'
AND password = '$password'");
if (mysql_num_rows($result) < 1) {
/* Access denied */
echo "The username or password you entered is incorrect.";
}
else {
$_SESSION['id'] = mysql_result($result, 0, 'id');
#header("Location: ./");
echo "Hello $_SESSION[id]!";
}
The easiest way to get your password storage scheme secure is by using a standard library.
Because security tends to be a lot more complicated and with more invisible screw up possibilities than most programmers could tackle alone, using a standard library is almost always easiest and most secure (if not the only) available option.
The new PHP password API (5.5.0+)
If you are using PHP version 5.5.0 or newer, you can use the new simplified password hashing API
Example of code using PHP's password API:
<?php
// $hash is what you would store in your database
$hash = password_hash($_POST['password'], PASSWORD_DEFAULT, ['cost' => 12]);
// $hash would be the $hash (above) stored in your database for this user
$checked = password_verify($_POST['password'], $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
(In case you are still using legacy 5.3.7 or newer you can install ircmaxell/password_compat to have access to the build-in functions)
Improving upon salted hashes: add pepper
If you want extra security, the security folks now (2017) recommend adding a 'pepper' to the (automatically) salted password hashes.
There is a simple, drop in class that securely implements this pattern, I recommend:
Netsilik/PepperedPasswords
(github).
It comes with a MIT License, so you can use it however you want, even in proprietary projects.
Example of code using Netsilik/PepperedPasswords:
<?php
use Netsilik/Lib/PepperedPasswords;
// Some long, random, binary string, encoded as hexadecimal; stored in your configuration (NOT in your Database, as that would defeat the entire purpose of the pepper).
$config['pepper'] = hex2bin('012345679ABCDEF012345679ABCDEF012345679ABCDEF012345679ABCDEF');
$hasher = new PepperedPasswords($config['pepper']);
// $hash is what you would store in your database
$hash = $hasher->hash($_POST['password']);
// $hash would be the $hash (above) stored in your database for this user
$checked = $hasher->verify($_POST['password'], $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
The OLD standard library
Please note: you should not be needing this anymore! This is only here for historical purposes.
Take a look at: Portable PHP password hashing framework: phpass and make sure you use the CRYPT_BLOWFISH algorithm if at all possible.
Example of code using phpass (v0.2):
<?php
require('PasswordHash.php');
$pwdHasher = new PasswordHash(8, FALSE);
// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );
// $hash would be the $hash (above) stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
PHPass has been implemented in some quite well known projects:
phpBB3
WordPress 2.5+ as well as bbPress
the Drupal 7 release, (module available for Drupal 5 & 6)
others
The good thing is that you do not need to worry about the details, those details have been programmed by people with experience and reviewed by many folks on the internet.
For more information on password storage schemes, read Jeff`s blog post: You're Probably Storing Passwords Incorrectly
Whatever you do if you go for the 'I'll do it myself, thank you' approach, do not use MD5 or SHA1 anymore. They are nice hashing algorithm, but considered broken for security purposes.
Currently, using crypt, with CRYPT_BLOWFISH is the best practice.
CRYPT_BLOWFISH in PHP is an implementation of the Bcrypt hash. Bcrypt is based on the Blowfish block cipher, making use of it's expensive key setup to slow the algorithm down.
Your users will be much safer if you used parameterized queries instead of concatenating SQL statements. And the salt should be unique for each user and should be stored along with the password hash.
A better way would be for each user to have a unique salt.
The benefit of having a salt is that it makes it harder for an attacker to pre-generate the MD5 signature of every dictionary word. But if an attacker learns that you have a fixed salt, they could then pre-generate the MD5 signature of every dictionary word prefixed by your fixed salt.
A better way is each time a user changes their password, your system generate a random salt and store that salt along with the user record. It makes it a bit more expensive to check the password (since you need to look up the salt before you can generate the MD5 signature) but it makes it much more difficult for an attacker to pre-generate MD5's.
With PHP 5.5 (what I describe is available to even earlier versions, see below) around the corner I'd like to suggest to use its new, built-in solution: password_hash() and password_verify(). It provides several options in order to achieve the level of password security you need (for example by specifying a "cost" parameter through the $options array)
<?php
var_dump(password_hash("my-secret-password", PASSWORD_DEFAULT));
$options = array(
'cost' => 7, // this is the number of rounds for bcrypt
// 'salt' => 'TphfsM82o1uEKlfP9vf1f', // you could specify a salt but it is not recommended
);
var_dump(password_hash("my-secret-password", PASSWORD_BCRYPT, $options));
?>
will return
string(60) "$2y$10$w2LxXdIcqJpD6idFTNn.eeZbKesdu5y41ksL22iI8C4/6EweI7OK."
string(60) "$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d."
As you might see, the string contains the salt as well as the cost that was specified in the options. It also contains the algorithm used.
Therefore, when checking the password (for example when the user logs in), when using the complimentary password_verify() function it will extract the necessary crypto parameters from the password hash itself.
When not specifying a salt, the generated password hash will be different upon every call of password_hash() because the salt is generated randomly. Therefore comparing a previous hash with a newly generated one will fail, even for a correct password.
Verifying works like this:
var_dump(password_verify("my-secret-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));
var_dump(password_verify("wrong-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));
var_dump(password_verify("my-secret-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));
var_dump(password_verify("wrong-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));
I hope that providing these built-in functions will soon provide better password security in case of data theft, as it reduces the amount of thought the programmer has to put into a proper implementation.
There is a small library (one PHP file) that will give you PHP 5.5's password_hash in PHP 5.3.7+: https://github.com/ircmaxell/password_compat
That's fine with me. Mr Atwood wrote about the strength of MD5 against rainbow tables, and basically with a long salt like that you're sitting pretty (though some random punctuation/numbers, it could improve it).
You could also look at SHA-1, which seems to be getting more popular these days.
I want to add:
Don't limit users passwords by length
For compatibility with old systems often set a limit for the maximum length of the password. This is a bad security policy: if you set restriction, set it only for the minimum length of passwords.
Don't send user passwords via email
For recovering a forgotten password you should send the address by which user can change the password.
Update the hashes of users passwords
The password hash may be out of date (parameters of the algorithm may be updated). By using the function password_needs_rehash() you can check it out.
Here's a PHP + CouchDB.apache.org login system that doesn't store plaintext passwords.
According to the advice that I've read, it should be totally secure.
CMS login code : https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/boot.php#L56
calls
https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/functions.php#L171
app(s) specific business code :
https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/ajax_login.php#L87
calls
https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/functions.php#L230
which in turn calls :
https://github.com/nicerapp/nicerapp/blob/2d479b3e22dce9e7073525481b775f1bf7389634/nicerapp/apps/nicer.app/webmail/recrypt.php#L2
and to edit the webmail app config data into the database :
https://github.com/nicerapp/nicerapp/blob/main/nicerapp/apps/nicer.app/webmail/ajax_editConfig.php

Categories