I've the following code:
$salt=uniqid(mt_rand(), false);
#Add data to tables
mysql_query("INSERT INTO accounts VALUES('$user', '".hash('sha512',$pass+$salt)."', '$salt', '$cookie_value')");
mysql_query("INSERT INTO passwordreset VALUES('$user', NULL, NULL)");
#cookie creation
#.....
#cookie update
mysql_query("UPDATE accounts SET cookie='$cookie_value' WHERE user='$user'");
I sanitize data from form using these functions:
$var = htmlentities($var, ENT_QUOTES, "UTF-8");
return mysql_real_escape_string($var);
Today I logged into phpMyAdmin and I saw that passwords and salts for all users are the same.
Don't remind me about deprecated mysql_* I know it, that's just quick draft.
String concatenation in PHP uses . not +. Thus:
hash('sha512',$pass+$salt)
Should be
hash('sha512',$pass.$salt) // or
hash('sha512',"${pass}${salt}")
This is PHP, $pass+$salt should be $pass . $salt
There's a few things I would comment on your current code:
Using the + operator (as opposed to .) on two strings results in the sum of both values cast to integer (if a string is not numeric it's cast to int(0)); when it's passed to hash() it gets cast to a string again, so your passwords will typically all be sha512("0"). I'm not sure why your salts all have the same value though, unless the column data type is INT in your database.
You can use uniqid(mt_rand(), true) to collect more entropy, resulting in a better salt.
You should hash passwords with a dedicated password hash, such as the BlowFish option in crypt() (make sure your column width is big enough); this way you can get rid of the salt column and you can choose how much work is required to verify the hash in a backward compatible manner.
The cookie column is for an auto-login feature I assume? It's better to create a separate table for this containing a randomized string as the primary key and a foreign key to your users table. This way you can support auto-login from multiple browsers.
error here
$pass+$salt
should be
$pass.$salt
. is used for string catenation in php
Related
what do you think about creating a verification link having a querystring made up with parameters as email and hash?
email is the html encoded plain string email of the registered user,
hash is the result value of the PHP function password_hash where the string for the first parameter is the unique id user number selected from the database with a fixed salt string.
I was thinking about also to toggle the salt, but one can guess the id user number for a given email, passing it to password_hash and obtaining a valid hash for the querystring (even if password_hash adds automatically a salt on his own.), so I think it isn't secure.
I thought something like this:
// the result of SELECT from the database (I use Mysql)
$id = '5';
// fixed salt string
$salt = 'xbrENiBq6kb87WrZYhxS';
$email = urlencode($email);
$key = $id . $salt;
$hash = urlencode(password_hash($key, PASSWORD_DEFAULT));
// the resulted querystring:
$url = "somesite.com/index.php?email=$email&hash=$hash";
then I will send that to the user email.
Please, can you tell me, what do you think about? Many Thanks!
Depends on your encoding, you can try with mixing up various types of encode to reach to the real value. Your final result to compare with the database must be a md5 string, this way you don't lose performance as well.
SELECT md5(your_field) FROM table
Sorry for the bad English.
I just want to hash my user password by taking the registration date_time as a salt. I am going to use this function:
function create_hash($pass, $created_date, $hash_method = 'md5') {
// the salt will be the reverse of the user's created date
// in seconds since the epoch
$salt = strrev(date('U', strtotime($created_date));
if (function_exists('hash') && in_array($hash_method, hash_algos()) {
return hash($hash_method, $salt.$pass);
}
return md5($salt.$pass);
}
And also similar method for password verification. But I am thinking about registration process. In my registration form I have only field of username and password and in my mysql database the creation date will be added automatically.
But when first time registration I also need the creation_date for hashing password? How to overcome this? So basically I am imagining I have to get the creation_date before inserting data in the database for salting. How to do this?
One approach I am thinking, in my registration form before calling this function I will do this:
$created_date = date("Y-m-d H:i:s");
$salt = strrev(date('U', strtotime($created_date));
But I am afraid is there going to be a fraction time difference between this salt time and creation_date time of database. If so then my password_validation will not work..
*** BY the way I know md5() is not secured one. So I dont need suggestion about md5 or other hashing algorithm. I am just interested about salting
Maeby is better idea to write a function that creates a salt
function salt($n=3)
{
$key = '';
$pattern = '1234567890abcdefghijklmnopqrstuvwxyz.,*_-=+##!&^%';
$counter = strlen($pattern)-1;
for($i=0; $i<$n; $i++)
{
$key .= $pattern{rand(0,$counter)};
}
return $key;
}
and when new user are registered to call this funtion and do something like this:
$salt=salt();
$hash_pass=md5(md5($_POST['password']).$salt);
mysql_query("INSERT INTO `my_table` SET `name`='".$_POST["name"]."', `pass`='".$hash_pass."' `regDate`=NOW() ");
When the user is loged in, you select the user data from table as name and compare his hashed password with posted password with his salt
The best salt you can have, is not derrived from any other parameters, and is not predictable. On a deterministic computer, the best you can do, is getting the salt from the random source of the operating system. This random source will collect real chance from events like user actions, boot time, and much more...
PHP already has a dedicated function password_hash() to generate a safe BCrypt hash with such a random salt. It includes the salt into the resulting hash-value, so you can store it into a single field in the database.
// 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);
Using a pepper can be a good idea as well, but there are better ways to add such a server side secret. Whether your pepper contains special characters or is just longer doesn't matter, the entropy is important. If you are interested you may have a look at my tutorial about secure password storing, on the last pages i tried to explain the pros and cons of a pepper.
I want to generate identifier for forgot password . I read i can do it by using timestamp with mt_rand(), but some people are saying that time stamp might not be unique every time. So i am bit of confused here. Can i do it with using time stamp with this ?
Question
What's best practice to generate random/unique tokens of custom length?
I know there are lot of questions asked around here but i am getting more confused after reading different opinion from the different people.
In PHP, use random_bytes(). Reason: your are seeking the way to get a password reminder token, and, if it is a one-time login credentials, then you actually have a data to protect (which is - whole user account)
So, the code will be as follows:
//$length = 78 etc
$token = bin2hex(random_bytes($length));
Update: previous versions of this answer was referring to uniqid() and that is incorrect if there is a matter of security and not only uniqueness. uniqid() is essentially just microtime() with some encoding. There are simple ways to get accurate predictions of the microtime() on your server. An attacker can issue a password reset request and then try through a couple of likely tokens. This is also possible if more_entropy is used, as the additional entropy is similarly weak. Thanks to #NikiC and #ScottArciszewski for pointing this out.
For more details see
http://phpsecurity.readthedocs.org/en/latest/Insufficient-Entropy-For-Random-Values.html
This answers the 'best random' request:
Adi's answer1 from Security.StackExchange has a solution for this:
Make sure you have OpenSSL support, and you'll never go wrong with this one-liner
$token = bin2hex(openssl_random_pseudo_bytes(16));
1. Adi, Mon Nov 12 2018, Celeritas, "Generating an unguessable token for confirmation e-mails", Sep 20 '13 at 7:06, https://security.stackexchange.com/a/40314/
The earlier version of the accepted answer (md5(uniqid(mt_rand(), true))) is insecure and only offers about 2^60 possible outputs -- well within the range of a brute force search in about a week's time for a low-budget attacker:
mt_rand() is predictable (and only adds up to 31 bits of entropy)
uniqid() only adds up to 29 bits of entropy
md5() doesn't add entropy, it just mixes it deterministically
Since a 56-bit DES key can be brute-forced in about 24 hours, and an average case would have about 59 bits of entropy, we can calculate 2^59 / 2^56 = about 8 days. Depending on how this token verification is implemented, it might be possible to practically leak timing information and infer the first N bytes of a valid reset token.
Since the question is about "best practices" and opens with...
I want to generate identifier for forgot password
...we can infer that this token has implicit security requirements. And when you add security requirements to a random number generator, the best practice is to always use a cryptographically secure pseudorandom number generator (abbreviated CSPRNG).
Using a CSPRNG
In PHP 7, you can use bin2hex(random_bytes($n)) (where $n is an integer larger than 15).
In PHP 5, you can use random_compat to expose the same API.
Alternatively, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM)) if you have ext/mcrypt installed. Another good one-liner is bin2hex(openssl_random_pseudo_bytes($n)).
Separating the Lookup from the Validator
Pulling from my previous work on secure "remember me" cookies in PHP, the only effective way to mitigate the aforementioned timing leak (typically introduced by the database query) is to separate the lookup from the validation.
If your table looks like this (MySQL)...
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id)
);
... you need to add one more column, selector, like so:
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
selector CHAR(16),
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id),
KEY(selector)
);
Use a CSPRNG When a password reset token is issued, send both values to the user, store the selector and a SHA-256 hash of the random token in the database. Use the selector to grab the hash and User ID, calculate the SHA-256 hash of the token the user provides with the one stored in the database using hash_equals().
Example Code
Generating a reset token in PHP 7 (or 5.6 with random_compat) with PDO:
$selector = bin2hex(random_bytes(8));
$token = random_bytes(32);
$urlToEmail = 'http://example.com/reset.php?'.http_build_query([
'selector' => $selector,
'validator' => bin2hex($token)
]);
$expires = new DateTime('NOW');
$expires->add(new DateInterval('PT01H')); // 1 hour
$stmt = $pdo->prepare("INSERT INTO account_recovery (userid, selector, token, expires) VALUES (:userid, :selector, :token, :expires);");
$stmt->execute([
'userid' => $userId, // define this elsewhere!
'selector' => $selector,
'token' => hash('sha256', $token),
'expires' => $expires->format('Y-m-d\TH:i:s')
]);
Verifying the user-provided reset token:
$stmt = $pdo->prepare("SELECT * FROM account_recovery WHERE selector = ? AND expires >= NOW()");
$stmt->execute([$selector]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($results)) {
$calc = hash('sha256', hex2bin($validator));
if (hash_equals($calc, $results[0]['token'])) {
// The reset token is valid. Authenticate the user.
}
// Remove the token from the DB regardless of success or failure.
}
These code snippets are not complete solutions (I eschewed the input validation and framework integrations), but they should serve as an example of what to do.
You can also use DEV_RANDOM, where 128 = 1/2 the generated token length. Code below generates 256 token.
$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
This may be helpful whenever you need a very very random token
<?php
echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>
I created a function in php that generates a hash from a number (id), and I need to check that there will be no collision (two or more ids have the same hash).
Which function I can use to verify that there will be no collision in the nexts 99999999 ids?
Thanks!
If your hash function works as supposed, and always generates the same output for the same input. And your inputs are restricted to 99999999 numbers, you could simply generate the hashes for those numbers and verify that there are no duplicates.
Although the nice solution would be to demonstrate mathematically that your hash function will produce unique results for those numbers.
If the hash can be totally random, try using the current timestamp in it as additional randomizer. For example:
$hash = sha1(microtime() * rand(1, 9999));
The chances of a duplicate coming out there is rather slim. Additionally, try setting the database field to be a UNIQUE field, ensuring a duplicate INSERT is impossibe. Then, to make things complete, you can create a loop that tries until it succeeds, like so:
// SHA1 values shouldn't need escaping, but it doesn't really hurt to be extra sure :)
$query = "INSERT INTO `table` (`hash`) VALUES('" . mysql_real_escape_string($hash) . "')";
// Let's try the insert with a max of 10 random hashes
$tries = 10;
while(mysql_query($query) !== true) {
if($tries <= 0) {
break; // Something is really failing, stop trying!
}
// If this point is reached, apparantly a duplicate was created. Try again.
$hash = sha1(microtime() * rand(1, 9999));
// Decrement the tries counter.
$tries--;
}
Ahoy Stack Overflow! This be mai first post...
I'm attempting to identify users with a salted unique public key.
Algorithm - Should I use uniqid(), sha256, sha512, something else? All hashes will be salted. NIST recommended SHA256, but I prefer to hear what others might suggest.
Generation - Does hash(SALT + AUTO_INCREMENT_PK + CREATED_TIMESTAMP) suffice? More entropy?
I'd use email, as it is unique for each user, however the user can modify their email address. I was also considering storing signup_email so that hashes would not have to be re-calculated.
MySQL Storage - Currently, our ID's are INT(255) auto_increment primary key's. As stated earlier, potentially hundreds of millions of keys. Depending on the crypto algo, I should have a fixed-size ID. Can I keep INT(255) or should I use CHAR(n)?
---------------------- Thanks for reading :) -------------------------------
One thing: If you don't trust the users with their IDs, sending them over GET or POST will not work; those are all visible to motivated users.
I would use SHA256 using a salt.counter.time string, and use the output to generate GUIDs for the actual id. This would minimize the possibility for collisions.
You will have to use CHAR for MySQL to store GUIDs.
See the comments at http://us2.php.net/manual/en/function.uniqid.php for more in-depth info. AFAIK GUID is not part of the PHP core so you have to fake it a bit.
If you are using user id as the way to allow a user to do anything with your service, if one user "guesses" the user id of another one, he'll be able to do whatever he wants with that one's account ?
You do not have any kind of other password or anything to go along that ?
Well, in that case, you need something quite unique, don't you ;-)
(Hoping I understood the question well -- but that might not be the case -- sorry, if it isn't)
What do you think of using Globally Unique Identifier (like, for instance, 61350955-9755-4AF3-8C19-6DBC42CA69E2) for your users ?
For an example of how they look like, take a look at http://createguid.com/
As a sidenote, that GUID is quite long ; which means lots of bytes in your DB, if you have millions users... So, it probably shouldn't be used as any kind of primary/foreign key.
What about using the smallest possible integer (that fits the number of users you'll have) as primary/foreign key, as that one will be duplicated in many places of the application ; and only have the "long user id" stored only once, in your user table ?
I wrote this class that gives you an unique id of 24 chars, compatible with the id field of MongoDB (and using the same logic to construct it). Might be useful in the future.
<?php
/**
* Generator for Mongo-like ObjectIds in pure PHP
* Author: Mauricio Piacentini
*
* Inspired by https://github.com/justaprogrammer/ObjectId.js
*
*/
class ObjectIdFactory
{
private $_datetime = null;
private $_machine = null;
private $_pid = null;
private $_increment = null;
public function __construct()
{
$this->_machine = str_pad(dechex(rand(0, 16777215)), 6, "0", STR_PAD_LEFT);
$this->_pid = str_pad(dechex(rand(0, 32767)), 4, "0", STR_PAD_LEFT);
$this->_increment = rand(0, 16777215);
//We need a DateTime object to get timestamps, cache it
$this->_datetime = new DateTime();
}
public function getNewId($forcedincrement = null)
{
if (is_null($forcedincrement)) {
$this->_increment++;
if ($this->_increment > 0xffffff) {
$this->_increment = 0;
}
} else {
$this->_increment = $forcedincrement;
}
$timestamp = $this->_datetime->getTimestamp();
$timestamp_final = str_pad(dechex($timestamp), 8, "0", STR_PAD_LEFT);
$increment_final = str_pad(dechex($this->_increment), 6, "0", STR_PAD_LEFT);
return $timestamp_final . $this->_machine . $this->_pid . $increment_final;
}
}
https://github.com/piacentini/ObjectId.php
Have you looked into using a UUID?
A quick google search yields some good resources/links.
Personally I use md5(uniqid(mt_rand(), true)) which will create 32 character identifier (a 128 bit hex number) that is extremely difficult to predict.