我使用了md5(rand(0,10000000))创建了一个盐值;(可能有更好的方法吗?)
在MYSQL中似乎无法使文本字段唯一。那么,我该如何检查是否已经为先前的用户使用了盐值?
或者,我应该基于当前日期/时间生成盐值?因为不可能有两个用户在完全相同的时间注册,对吗?
MySQL的索引在文本字段上是有长度限制的,它们不像char/varchar字段那样自动覆盖整个字段,因此在文本字段上使用“唯一”键没有实际意义。
但是,如果您要存储由MySQL生成的哈希值,则不需要文本 - 哈希结果是纯文本,因此只需使用固定长度的char字段:
mysql> select length(md5('a')), length(sha1('a'));
+------------------+-------------------+
| length(md5('a')) | length(sha1('a')) |
+------------------+-------------------+
| 32 | 40 |
+------------------+-------------------+
然后你可以对该字段应用唯一约束。
对于盐来说,独一无二比长度和可预测性更重要。你要假设攻击者已经知道了盐。
最好使用通用唯一标识符(UUID),在 php 的 uniqueid() 函数的文档页面上有生成通用唯一标识符的示例。UUID 优于随机字符串的优点在于它是人类可读的且长度固定,因此可以将其存储在 varchar 字段中,并使用唯一索引来确保没有重复。
使用 MD5 哈希时间是生成唯一值的常见方法,因为它具有固定长度且易于阅读。但是,最好生成一个固定长度的随机字符串并自己将其编码为十六进制。哈希不是为了独一无二而设计的,而是为了不可逆转。使用哈希函数会导致冲突,尽管 SHA1 比 MD5 冲突更少。
盐的长度实际上只是一个因素,因为盐越长,它就越有可能是通用唯一的。
md5()是一个有缺陷的算法,不应该使用。
rand()略有缺陷,因为它基于系统时钟。
更好的方法是:
function generateRandomKey()
{
return base_convert(uniqid(mt_rand(), true), 16, 36);
}
编辑:如果有更好的方法或者我错了,请展示你的做法。我真的很感兴趣,想知道是否我太不自信了。
通常情况下不会经常生成盐字符串。因此,当您首次生成它们时,应该做得很好。长度更长、更随机的字符串更好。
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 user set salt = :salt, password = sha1(concat(:password, :salt)) where id = :id limit 1;
您可以同时检查密码是否正确并获取用户数据。
select * from user where id = :id and password = sha1(concat(:password, salt)) limit 1;
你可以使用类似 http://php.net/manual/en/function.uniqid.php 的方法来生成 UUID。或者时间戳也是一个不错的选择 - 甚至可以结合 IP 地址或类似信息。