2010-01-09 90 views
16

我创建一个PHP的网站,该网站包括用户注册,和我想知道的“确认电子邮件”代码的最佳做法。通过生成代码,并在一封电子邮件中,他可以再使用,以激活其账户将其发送到用户我做到这一点 -最佳做法电子邮件确认代码

新用户必须确认他们的电子邮件地址。而不是存储在数据库中这一关键,我使用一个方便的小解决办法:代码的结果是:

md5("xxxxxxxxx".$username."xxxxxxxxxxx".$timestamp."xxxxxxxxx"); 

其中$时间戳是指用户创建时间。总的来说,我对此很满意,但后来我开始想,这足够安全吗?那碰撞的可能性呢?我还需要生成密码重置代码等。如果我使用了类似的方法,碰撞可能会导致一个用户无意中重置另一个用户的密码。这并不好。

那么,你如何做这些事情?我的想法是以下格式的表:

codePK (int, a-I), userID (int), type (int), code (varchar 32), date (timestamp) 

其中“类型”是1,2或3,意思是“激活”,“电子邮件变”或“密码重置”。这是做这件事的好方法吗?你有更好的方法吗?

使用类似上述的方法,可能我会自动删除任何超过两天老不使用的cron的作业?我的主机(almostfreespeech.net)不支持它们。如果可能的话,我想避免在外部主机上执行cron-job,而wget是一个删除内容的脚本,因为这只是messy = P。

谢谢!
麻辣

更新:
为了澄清:我已经意识到可靠而安全地去了解这个任务的唯一方法是使用一个数据库,这是原函数试图避免。我的问题是如何构建表(或多个表)。有人建议我不要使用codePK,只需将代码设为PK即可。所以总之,我的问题是:这是你做的?

+0

电子邮件确认代码的最佳做法?让他们离开... – Leo 2010-01-09 13:52:20

+1

用户在文件上有一个有效的电子邮件地址是很重要的,如果仅用于密码重置。虽然人们可以声称“这是用户的责任”,但这是我们正在处理的真实世界......只要第一个“永远不会忘记密码”的人忘记密码,大概一到两周后,我的系统将是“最愚蠢的”。 – Mala 2010-01-10 12:49:30

回答

10

当我需要这些类型的招数是正常的两个原因之一,无论你提到:

  1. 作为用于发送到用户
  2. 作为用于密钥验证电子邮件的关键密码重置链接

当然,还有许多其他场合你会考虑使用这种结构。

首先,你应该总是使用某种隐藏的盐,只有你知道。请注意,这个盐对每个用户应该是不同的。盐可以例如计算为sha256(something random)。然后将该盐与用户名和密码一起存储在数据库中(用盐进行散列)。

发送密码重置链接时我会做的是创建另一个盐(不要让用户访问任何用你的盐散列的东西,他知道他的密码,所以使用暴力,他可能会找出你的盐) 。另一种盐,实际上只是一个随机字符串的散列(你可能想在这里找到md5,正如你提到的那样,长度是一个问题),你应该保存到数据库中。

通常情况下,您可以避免在用户表中添加额外的列。但是,这也存在一些问题,主要是一旦密码被重置或用户被激活,您将从数据库中删除密钥,导致大多数行具有空值,这反过来又带来一些其他麻烦。

这本质归结为:采用了独特的换的用户盐(也许是一个全球性的,秘密的盐)

  • 哈希用户的密码。
  • 通过散列一些随机或伪随机来源(如时间戳,mt_rand()甚至random.org)来生成密钥,如果您真的想要随机的东西。
  • 切勿使用全局盐,或者是对用户唯一的散列任何用户获得访问权限,包括密码重置键,启动键等

请不说我怎么也不盐意味着安全专家,我可能已经忘记了很多事情,而且我可能提到了一些非常糟糕的做法。只是我的5美分;-)

1

这是足够安全的权利,直到在那里你发布你在互联网上法点!这是因为你依靠默默无闻的安全,这不是一个好主意。

你应该使用某种加密散列函数或MAC理想,其中包含只认识你一个秘密密钥。

+2

显然这个'x'实际上并不是代码中的'x' – Mala 2010-01-09 12:30:04

+0

在这种情况下,你已经创建了一个密钥散列函数,你很好。我应该可能已经猜到了......;) – 2010-01-09 12:40:40

+0

;)对不起,我以为我已经指定 - 但显然我编辑的位,我明确表示,在任何情况下,我想知道如果表结构/使用所有代码的单个表是合适的 – Mala 2010-01-09 12:56:42

0

为什么不让代码字段唯一索引?所以永远不会有共谋?

另外,如果你不需要来自用户的输入创建的哈希和它匹配的数据库散列(电子邮件确认,密码重置等) - 你可以随机字符串添加到哈希身上,就像 md5('xxx'.$username.'xxx'.time().'xxx'.rand())

+0

那么我不能在没有使用数据库的情况下检查它 - 该函数的全部要点是避免使用它们。如果我打算使用数据库,那么我可以让整个字符串完全是随机的 – Mala 2010-01-09 12:36:21

2

为什么使用任何用户的数据作为授权密钥的基础?

我认为你正在存储数据库中的去激​​活数据,为什么不只是添加一个简单的随机密钥的额外记录(也许是一些额外的操作md5'ed uniqid),然后检查呢?

+0

原来我试图避免必须使用数据库 - 因此使用userdata – Mala 2010-01-09 12:35:24

+1

说实话,我不能帮助,但认为数据库是要走的路,否则你将哈希/操纵一个相当有限的静态数据池。 – 2010-01-09 12:39:37

0

为什么不要求用户输入他们的用户名他们的代码,从而消除碰撞的任何问题?你不会因为安全问题而失去任何东西,因为你仍然在要求他们从电子邮件中获得的钥匙,但是你会阻止他们重置其他用户的密码。

+1

理想情况下,他们只需点击电子邮件中的链接即可。我不希望这太可怕了= P – Mala 2010-01-09 12:36:56