views:

285

answers:

6

I've created a salt using; md5(rand(0,10000000)); (there is probably a better way?)

There doesn't seem to be possible to make a text field unique in MYSQL. So how do I check if the salt has already been used for a previous user?

Or should I generate the salt based on the current date/time? as it is impossible for 2 users to register at exactly the same time correct?

A: 

md5() is a broken algorithm and should never be touched.

rand() is slightly broken because it is based on the system clock.

A better method is:

function generateRandomKey()
{
    return base_convert(uniqid(mt_rand(), true), 16, 36);
}

Edit: If there is a better way or I'm wrong, please show me your way of doing it. I'm genuinely interested, and would like to know if I'm being insecure.

Coronatus
For another more widely accepted view see http://stackoverflow.com/questions/2329609/is-it-safe-to-store-passwords-hashed-with-md5cryptoserviceprovider-in-c
le dorfier
@ledorifier, the link you posted is about hashing passwords, not generating random salts.
Marcus Adams
And http://stackoverflow.com/questions/157998/whats-the-difference-between-sha-and-md5-in-php. I was referring to " ... never be toucheed."
le dorfier
Generating the salt does not need to be secure, if the attacker really wants to find the number that created the salt then that's up to them to waste their time.
Jonathan
A: 

you can use something like http://php.net/manual/en/function.uniqid.php to generate a UUID. or the timestamp is also a good one - maybe even timestamp and ip address or similar.

oedo
A: 

Generate the salt with SHA1 using user id and datetime.

Ivo Sabev
+1  A: 

For a salt, uniqueness is more important than length and predictability. You assume the attacker has the salt.

A universally unique identifier (UUID) would be best, and there are examples that generate universally unique identifiers on the doc page for the php uniqueid() function. A UUID has the advantage over a random string in that it's human readable and a fixed length, therefore you can store it in a varchar field and use a unique index to ensure there aren't ever duplicates.

Hashing the time with MD5 is a common method to generate unique values because it has a fixed length and is human readable. However, it makes more sense just to generate a fixed length random string and encode it into hex yourself. Hashes aren't designed for uniqueness so much as they're designed not to be reversible. Using a hashing function guarantees collisions, though there will be less collisions with SHA1 than MD5.

The length of the salt is really only a factor because the longer the salt, the more likely it is to be universally unique.

Marcus Adams
should be some freak coincidence 2 users who happen have the same salt decide to have the same password, then
Jonathan
@Jonathan, if there are duplicate salts with different password digests, I'll attack those accounts first since it gives me a better chance at finding a successful collision. Two accounts with the same salt and password would give the attacker two for the price of one, but the attacker doesn't get any breaks.
Marcus Adams
+1  A: 

MySQL's indexes are length-limited on text fields, they don't go whole-field automatically like they do on char/varchar fields, so there's no practical way to use a 'unique' key on text fields.

But if you're storing hashes generated by MySQL, then you don't need text - the has results are plain-text, so just use a fixed-length char field:

mysql> select length(md5('a')), length(sha1('a'));
+------------------+-------------------+
| length(md5('a')) | length(sha1('a')) |
+------------------+-------------------+
|               32 |                40 | 
+------------------+-------------------+

and then you CAN apply a unique constraint to that field.

Marc B
A: 

You generally dont generate salt strings often. So when you first generate them, you should do a good job. Longer and more random strings are better.

function generateSalt($length = null)
{
  if (!is_int($length) || ($length < 1)) $length = 250;
  do {
    $salt[] = chr(mt_rand(0, 255));
  } while (--$length);
  return implode('', $salt);
}

update query for new password

update user set salt = :salt, password = sha1(concat(:password, :salt)) where id = :id limit 1;

you can check if the password is correct and get the user data at the same time.

select * from user where id = :id and password = sha1(concat(:password, salt)) limit 1;
OIS