现在,我正在生成一个存储在数据库中的25个字符串,该字符串只能使用1次,并且在用户注册后30分钟到期。

http://example.com/security/activate/ZheGgUNUFAbui4QJ48Ubs9Epd


我执行快速数据库查找,其逻辑如下:


如果未激活该帐户并且未验证电子邮件并且验证码仍然有效,则激活该帐户,标记该电子邮件地址为已验证,并将验证码标记为已使用。例如,每72小时就会刷新过期和已使用的验证码。
这是为了告诉用户单击的激活链接已过期,例如,如果他第二天看他的电子邮件并尝试该链接。

我应该在URL中包含用户UUID吗?我需要添加其他内容吗?

我考虑过确保在按下激活链接时确保注册表中的IP地址与请求的IP地址匹配,但是对于我来说,我主要阅读我的电子邮件在我的手机上放这种东西,对UX来说会很痛苦。

评论

不要打扰保持/检查IP地址。 ip地址可以在短时间内更改的原因有很多-只需从一家咖啡店转移到隔壁的咖啡店即可。

除其他答案外,始终提供一种方法来表明使用给定电子邮件地址的注册是错误的/不应存在。唯一可能要确认的验证电子邮件不是很有用。从个人经验来看:我的名字很普通(在匈牙利),我不断收到要求其他人提供帐户确认信的邮件,这些人错误地提供了我的地址而不是他们的地址,并且大多数人都不会被拒绝。最好的部分是当他们每周不断“提醒”我以确认我从未做过的帐户时。

30分钟有点苛刻-我可能会设法在这段时间内从这里到达我的电子邮件,但是回到网络的距离却是相同的,而且我敢肯定,我不能合法地管理该回合不到50分钟即可到达。

30分钟?为什么?我看不到您如何完成任何工作。我预计至少需要24小时才能激活帐户...

的确,30分钟太短了。您的邮件甚至可能不会在这么短的时间内传递到用户的邮箱。 24小时是正常的,届时几乎所有人都会收到电子邮件。

#1 楼

如何生成包含在URL中的25个字符串?它是完全随机的,还是基于当前时间,还是基于用户的电子邮件?它应该是随机的并且不可猜测。

您应确保验证页面实际呈现(不仅仅是发生GET请求)。诸如chrome(和防病毒程序)之类的浏览器通常会加载URL,而用户不会出于安全原因而明确点击它们以进行预取或扫描。

这可能导致恶意行为者(Eve)想要使用他人的电子邮件(Alice)进行帐户创建的情况。
夏娃报名参加了,爱丽丝收到了一封电子邮件。爱丽丝打开电子邮件是因为她对自己未请求的帐户感到好奇。她的浏览器(或防病毒软件)在后台请求该URL,从而无意中激活了该帐户。

我将在页面上使用JavaScript来验证页面的实际呈现,并在电子邮件中包含一个链接,以便用户报告未创建此帐户。

评论


我还要补充一点-如果您具有更改电子邮件功能,请确保在用户更改其电子邮件地址时清除所有现有令牌。这样可以防止用户使用有效的电子邮件进行注册,将其电子邮件更改为无效帐户然后单击您的注册链接。

–戴夫·萨奇
18年5月5日在6:38

我会在页面上使用JavaScript来验证实际呈现的页面,值得注意的是JS可以阻止用户访问。如果是的话,那么也许他们会在链接上以为已验证该帐户的想法为由,但是当他们尝试登录时,例如下周,他们发现该帐户已被删除。

– VLAZ
18年11月5日在7:09

有时页面上有一个大按钮“激活帐户”。这清楚表明该帐户尚未激活,解决了JS off问题,并允许一次在其旁边有一个“取消请求”。

–卡利莫
18年11月5日在7:25

如果要保证不会意外激活电子邮件地址,请使用POST请求。最好不要让GET请求更改服务器状态。另请参阅《末日蜘蛛》。

–传真
18年5月5日在13:40

听起来不错。我的结论是,不应通过HTTP GET进行帐户激活。实际上,我认为这违反了HTTP动词语义

–usr-local-ΕΨΗΕΛΩΝ
18年5月5日在18:07

#2 楼

添加到Daisetsu的答案中(因为我还不能写评论)。

创建表单并要求用户创建密码也不错。这样,用户将需要输入新密码,并在提交新密码后激活帐户。它将解决后台防病毒检查。

至于URL,只要是随机字符串,就应该足够。您一次拥有两个相同内容的键的可能性很小。

检查IP地址不是很好,因为某些ISP可能会轮换IP地址,因此您将阻止想要注册但不能注册的人。

评论


实际上,我已经在开发的最新网站上使用了此方法。要添加此内容,您可以使用生成的所有“随机字符串”(aka:令牌)创建数据库。这样,您可以保证不会重复使用密钥。如果您将令牌用作主键,则MySQL返回错误1062,这使得验证键是否存在非常容易。

–伊斯梅尔·米格尔(Ismael Miguel)
18年7月7日在17:57

@DavyM谢谢。起初,我试图创建一个添加项,但后来又加入其中并创建了有用的答案。好像是这样。

– Maros D.
18年11月7日在18:21

@IsmaelMiguel是的。如果您想确保将来不会使用该密钥,也可以执行此操作。您还可以为其添加一些时间戳,以便令牌可以在一段时间后重新使用(每月删除旧令牌)。

– Maros D.
18年7月7日在18:24

虽然这似乎是一个好主意,但仍然存在一个问题,即有人拥有令牌并可以重复使用它。假设用户正在尝试通过使用主题来查找带有令牌的电子邮件。用户会从其他电子邮件中记住它。并找到带有令牌的随机密码重置电子邮件。用户单击它。由于您删除了令牌并重新创建了新令牌,因此,如果您不走运,该令牌实际上可能是给其他人的。然后他们更改其他人的密码。现在他们可以使用其他帐户访问了。

–伊斯梅尔·米格尔(Ismael Miguel)
18年11月7日在18:59



@IsmaelMiguel你说得对。我没有想到这一点。

– Maros D.
18年7月7日在19:31

#3 楼

您的电子邮件应包含激活码,而不是链接。您不应在电子邮件中发送用于帐户管理的任何链接。如果您(合法方)将电子邮件链接(特别是带有长串随机字符的电子邮件)链接到用户,那么您就很难区分合法电子邮件和网络钓鱼尝试。

激活码应包括易于复制和粘贴的字符(无空格,'+','-'等),也易于手动输入。字母和数字有效。激活码的安全性取决于它的不可预测性。您可以使用系统的安全随机数生成器来导出该值,就像使用cookie的会话ID一样。

激活码应存储在数据库表中,每一行都有一个配置文件ID,激活码*,以及到期时间。您不想将配置文件ID或有效期放在Cookie,URL或激活码中。**恶意用户可能篡改该ID来接管另一个帐户。您需要在服务器端验证这些内容,因为您的安全策略不能盲目地信任客户端数据。

激活代码应通过POST请求提交。完成其余注册后,自动将用户重定向到帐户激活URL(可以是https://example.com/security/activate)。如果他们不小心关闭了帐户激活页面,可以轻松再次访问该页面。

您不希望GET请求执行任何操作。无需用户交互(由于浏览器预取或电子邮件安全服务),链接可能会被访问​​。此外,在URL中放置机密可能会泄漏敏感数据。 (通过引荐标头或浏览器插件。)

不用担心IP,它仍然可能会更改。 (与用户代理相同。)

我不知道是否需要取消功能。我什至不使用未经请求的电子邮件中的取消订阅链接,因为这可能是确定是否有人与电子邮件地址相关联的一种方法。如果激活码足够长,可以抵抗暴力破解,或者如果您限制激活码的尝试次数,那么仅让激活码最终失效,我看不出什么危害。

另一方面减少不需要的帐户激活电子邮件的数量可能会很好。您可以礼貌地限制发送到某个地址的电子邮件数量。每天最多说3个,每月最多说5个。 (我不知道这是太高还是太低。)作为非用户,我只是将来自您网站的电子邮件自动删除或自动标记为已读。

有很多不必要的激活电子邮件要考虑的变量数量。最有可能与用户体验有关而不是安全性。您可以提供“取消我的激活,并且从不发送电子邮件至该地址,因为我永远也不会注册”功能,但是这样做存在明显的问题。 (您仍然需要验证此人的电子邮件。)

*对激活码进行哈希处理并没有什么坏处。与密码一样,哈希也并非至关重要,因为每个代码都只能使用一次,不包含任何私人信息,由服务器随机生成并过期。

**实际上,您可以防止篡改使用加密的MAC,但是我强烈不建议大多数开发人员尝试这样做。密码学很难正确解决。

评论


@HypeWolf 36并不短; 36 ^ 25对我来说足够大了。

– wizzwizz4
18年5月5日在20:33

@HypeWolf偏执狂和良好的安全性不一定相关;您使用的是GET请求来触发操作(这时,您应该转到带有发送POST请求以触发操作的按钮的页面)。

– wizzwizz4
18年11月5日在20:39

@Tom其他答案和评论指出GET可能是由错误触发的,例如通过安全扫描仪。我也同意,总的来说,我们应该劝阻用户不要单击电子邮件中的链接,尽管直到Paypal使他们的真实声明看起来不太像网络钓鱼尝试为止,这可能是一场失败的战斗。

–IMSoP
18年11月6日在15:13

@HypeWolf字母大小并不重要。仅可能的代码数。如果您在个人资料/验证码中添加了一个计数器,并在出现过多错误答案后使计数器无效,那么您可以使用短得多的代码。我什至会放弃元音以避免随机拼写真实单词。将O和0视为相同,I,L和1也视为相同。删除空格/分隔符。等等。可能的不同代码的数量是影响安全性的唯一因素。其他任何决定都是用户体验问题。

–未来的安全性
18年11月6日在22:35

@Tom您说对了,所有这些都不起作用。但是,将代码放入GET参数与将代码放入POST参数很重要。如果您不检查同一会话cookie,则GET可能会误判。但是,可能有人在笔记本电脑上注册,但在手机上阅读了电子邮件。或者,如果cookie在注册后但在验证之前关闭浏览器,则可以清除cookie。除此之外,唯一的安全差异是您是否为网络钓鱼准备好用户。 (我们不是在谈论密码重置系统。那是非常不同的。)

–未来的安全性
18年11月6日在22:51



#4 楼

与使用安全实践的任何事物一样,暴露和威胁评估是您必须针对自己独特的环境和用例进行评估的内容。现有的答案为该评估提供了很多接触点,以及避免发生问题的指针,例如GET触发的动作。相反,我将更多地关注UX的“用户”部分。我还将触及页面上散布的许多评论。

通过电子邮件触发的操作“验证”的确切原因也是要考虑的因素。如果您只是确认这是一个有效的电子邮件地址,那么几乎所有的操作就足够了。验证电子邮件,创建帐户的意图以及接收电子邮件的用户是发起帐户创建的用户,都需要付出更多的努力。

您不必费力地进行一次“努力”但是,是发送提醒电子邮件。假设用户有意创建了该帐户,使用了一个有效的电子邮件地址,并希望将该帐户用于提供帐户的任何目的,那么他们将寻找电子邮件,并会尽快进行激活过程。如果链接,代码或任何其他内容在等待电子邮件时因其他事项而陷入歧途,则在使用它们之前可能会过期。在这种情况下,允许他们重新发送验证电子邮件即可。但是,提醒电子邮件很可能是垃圾邮件触发手段,而不是给用户带来好处。

作为用户,我很高兴有可用的选项来激活/验证我的帐户。我遇到的最常见的选项集是,该电子邮件提供一个可以单击或剪切并粘贴的链接,以及一个可以在网站上我的帐户设置区域内的页面上的表单字段中输入的确认代码。确认码很少是,除了5到9位数长的整数。

作为用户,给我最自信的验证电子邮件是那些在URL(/verify?l=lgGS2SBjMTU4NjkwNjYxMjI5MDBhZDk2YjEyMzMzYjNhZmQxOb)中具有加密外观的哈希的电子邮件,这使我进入到我唯一的页面,然后我必须输入以前使用的密码创建帐户。使用哈希可以确认链接是在电子邮件中收到的,没有被猜测或强行使用,因此可以验证电子邮件是否有效。在执行任何其他操作之前,该唯一页面的提供允许服务器将发送给它的电子邮件标记为“有效”,而无需确认帐户的创建。与某些防病毒,恶意软件检测或预取操作相反,输入密码可确认另一端有参与者。密码的有效性在合理的确定性范围内确认电子邮件的收件人和帐户的创建者是同一个人。

建议的30分钟的时限似乎确实是严重,而如果威胁评估表明您在24小时内无法接受暴露,则24小时可能会太大。我认为2个小时对几乎所有用户的环境都足够。如果重新发送验证的功能可用,则尤其如此。即使使用与远程POP帐户建立14 kb / s连接的移动设备,也应能够在120分钟内完成交换。

使用JS验证人为操作可能会出现问题。 JS可以关闭,并且比许多Web开发人员想知道的更多。其次,即使在浏览器中启用了JS,广告拦截器也可以基于每个站点或每个源来阻止JS。我在全球范围内阻止了许多JS来源,包括Google的分析脚本,并将一些网站(例如Stack Exchange)列入白名单,我愿意在其中使用我的数据来支持它们。

在电子邮件中或确认页面上包含链接以“取消”帐户是浪费的。在电子邮件中,它实际上适得其反,因为建议您单击有关您不想要的帐户的链接是一个好主意。相反,电子邮件中应包含文字说明,表示无所事事将导致帐户无法确认,甚至可能被删除。确认页面上的取消链接甚至更糟,因为它奖励不良行为。作为旧版Google snafu的一部分,我经常收到发送到另一个Gmail帐户的电子邮件,并且我认为相反的情况也适用于其他帐户。 [[过去,Google不会合并点缀和不带点缀的用户名,因此我的点缀用户名和其他人的不带点缀的版本是不同的帐户,但是Google偶尔会滑倒,反正我会收到他们的电子邮件:(]

您将“经过验证的”电子邮件地址与激活链接紧密结合的程度取决于您的使用案例和威胁评估。当更改关联的电子邮件地址后必须重新验证我的帐户时,我会感到非常恼火。我对流程的接受程度与我的帐户“值”成正比。对于我的银行帐户,PayPal等,我100%接受。但是在PcPartsPicker上,我大约接受15%

对于IP和/或用户代理,请忽略它们。作为一个例子,我经常使用移动设备查看站点并选择创建帐户。不会,但是在上面打开电子邮件。如果我创建了一个帐户,并得知我需要以某种方式进行验证或确认,并且电子邮件是提供的方法,我将等你直到我回家在台式机上打开电子邮件时,我才可以检查它。 IP和用户代理将因此非常不同。但是,我的密码将保持不变,并且足以验证我作为原始帐户创建者的身份。

请记住,电子邮件与时代广场的Jumbo-tron一样安全,然后进行相应操作。

评论


“确认码很少是,除了5到9位数字的整数。”这很重要。一次性代码会在30分钟后过期,因此25个字符完全是多余的。甚至6个字母数字字符都需要60万个POST /秒,才能在30分钟内覆盖50%。限制验证服务以延迟百分之一秒,并且对手最多可以扫描0.008%的密钥空间,假设他们在整个30分钟内都消耗了整个验证服务,则他们就有12,000的几率...

–临时狼
18年11月6日在20:26

@TemporalWolf是否包含对手可以并行扫描的事实?

–PaŭloEbermann
18年11月7日,0:31

@PaŭloEbermann如果您限制验证服务,那么这将限制其可以扫描的密钥数量。这都是关于平衡风险的。要银行吗?可能不会。对于小型企业?大概。当任何滑动窗口中的新帐户数量超过100个左右时,我就会开始考虑增加它。这仍然提供了小于1%的机会猜出任何人的代码,以及100%的机会检测到该尝试(假设进行了一些监视)。 6个字符是〜32位的熵。 10是〜52。 25是〜130。您可以通过验证服务来控制蛮力速度。

–临时狼
18年7月7日在19:16

#5 楼

验证电子邮件需要几件事,既要合理安全又要便于用户使用:


用户必须清楚发生了什么事。被解释为什么会收到此电子邮件(例如“ ...某人在我们的网站上注册了BLAH帐户,这是为了确认...”)排除了大多数愚蠢的错误,并可以合理地阻止钓鱼邮件。希望您知道您是否刚刚在某项服务中注册了帐户(除非您完全痴迷)。因此,收到一封说明了这一点的电子邮件就不足为奇了。另一方面,这样一封知道您未在任何地方注册的电子邮件应该足够可疑(很好,希望如此)。如果不是这样,并且用户单击通过电子邮件发送的任何随机链接,则无论如何您将无能为力,不知道会向或向不知道的人发送哪些邮件。
(非顺序!)令牌,该令牌较长(足够长以确保它不可猜测且不会发生冲突)传递回服务器。该令牌也存储在数据库中以进行比较。它到底有多长以及它具有什么特定格式都没有关系。它不需要人类可读。任何大于20个左右的字符(假设使用base64编码)都可以使用,但是我会使用40个字符来确定,因为它实际上并不会花费您任何费用。 240位伪随机字符串几乎可以保证是唯一且不可猜测的。为了方便起见,我实际上将其设置为链接,并且该链接的确可以是GET请求。是的,用户不应该单击链接,但他们还是会这样做(您不会对其进行教育!)。另一方面,与必须复制粘贴一些晦涩的字符串相比,用户体验要好得多。
一个带有“单击以确认”按钮的HTML表单,该按钮可以重新输入数据,这保证了URL的推测性加载不会使东西(无意甚至恶意)发生,拥有邮件地址的用户不会知道关于。该表格应显示合理数量的信息,以便用户知道发生了什么,并作为最后一次触发“ WTF?”的机会。万一用户没有注册该帐户。
如果您担心使用机器人,则可以在表单中添加“我不是机器人”(个人,我认为那是多余的,但您认为里程可能会有所不同。)
有效的到期日期(或时间)。什么合理没有人能给出一个明确的普遍答案。我说从4个小时到两天的时间可能都不错。通常,当您注册某件东西时,会在5到10秒钟内收到确认邮件,然后坐在计算机前等待确认。如果您使用的是典型的偏执狂模式公司邮件,则可能会更长一些,这种公司邮件需要花费几分钟来扫描每一封外部电子邮件。不过,您可能会打个电话,或者不得不去中间的某个地方,因此让令牌有效期为几个小时或一天肯定不是错误的。另外,邮件可能会延迟(很少见,但确实会发生)。另一方面,通过将确认令牌(并阻止帐户名!)保留数周,数月或数年,浪费磁盘空间没有任何意义。不仅浪费资源毫无用处,而且一两天之内没有确认的人可能永远也不会。或者,他们可以重新注册一次。真的,什么都不花钱。


#6 楼

根据您的问题,我假设以下假设,如果其中任何一个无效,那么我的回答也是:


用户在您的网页上注册并在注册时设置用户名/电子邮件和密码
电子邮件验证的目的是确保用户使用正确的电子邮件地址注册。
激活帐户是电子邮件链接唯一要做的事情。它不允许密码重置或其他类似功能。基本上,它执行“ UPDATE用户SET已激活=正确的WHERE令牌=%1”

在这种情况下,从安全角度来看,您的方法绝对正确。 25个字符可为您提供足够的保证,以防止随机碰撞和强行尝试;一次性使用可防止嗅探和类似攻击,并且反正某人唯一可以做的就是激活帐户。

您可能会如果出于性能原因有很多用户,则希望包含用户ID(更新:并非如此,请参见下面的注释)。通过字符串标记查找用户将导致表扫描,同时通过ID查找用户,然后提取并比较字符串是索引查找。但是,这确实会公开用户ID,您可能会或可能不想这样做。

评论


“通过字符串令牌查找用户将导致表扫描”-如果在令牌列上放置索引,则不会。文本索引可能比数字索引稍慢,但是根本没有理由进行表扫描。

–IMSoP
18年6月6日在15:15

索引不是免费的。您是否会在表上放置一个索引,该索引将在每一行的生命周期中使用一次? (同样,对于大多数行,它都将为空。嗯,因此,它实际上可能具有一些良好的性能)

–汤姆
18年11月6日在23:12

您可以为激活令牌创建一个单独的表(带有索引),而不是将其放入用户表中。

–PaŭloEbermann
18年11月7日,0:33

为激活令牌使用单独的表:令牌,uid,到期。在使用令牌的HTTP请求中,运行DELETE FROM TokenTable WHERE到期
–user135823
18年11月7日在1:52

同意,@ GypsySpellweaver将是一个很好的解决方案。

–汤姆
18年7月7日在6:34