作者:冬冬
本文为原创文章,转载请注明作者及出处
几乎每个网站、App 都有自己的用户帐号系统。如果你是刚刚接触帐号系统的产品经理与开发人员,千万不要认为帐号设计只有表面的“登录注册”、“找回密码”等功能,它远比你想象的复杂的多。
沪江作为老牌在线教育网站,帐号系统的历史已经超过了十五年(从没有丢失过注册用户的信息),经历过各种各样的问题和挑战,也踩过不少坑。最近,我总结了关于沪江帐号系统的工作经验,并结合了业界数据和经典案例,在这里分享关于帐号系统设计的若干遐想。
用什么标识唯一用户?
根据惯例,帐号体系通常会选择用户名作为唯一的不可重复的标识。你可以看到,一些产品仍在用类似的概念去标识用户的唯一性(比如微博中,微博昵称是全局唯一的)。沪江也是这样,这种情况自沪江建立帐号体系开始就持续到了现在。但是今天,很多系统正在计划放弃历史悠久的用户名,转而使用手机号码去标识用户的唯一性。
为什么要用手机号标识用户?那是因为,手机号有着明显的优点,用户名又有着很多很难解决的缺点:
-
手机号容易获取。移动时代,几乎每个人都有自己的手机号码。
-
没有人不记得自己的手机号,但是用户名可能会忘记。
-
在互联网实名制的大环境下,使用手机号也能很好地满足政策上的要求。
-
对于一些产品,用户名往往被用于社交场景中的外显,为了更好的“展示”自己,用户都会想去改一个更称心的用户名。
-
但由于用户名是唯一的,“好听”的用户名就那么几个,取一个没有重复用户名就会变的非常困难。
-
取了一个自己不熟悉的用户名,用户名变得更容易忘记。
-
即便是社交产品,也很少有“广场式社交”,更多是小的社群场景,昵称在这样的场景中没有必要保持全局唯一性。
所以,大多数场景下完全可以用手机号标识用户,并用“昵称”去取代用户名原先在社交场景中的地位。昵称可以重复,且输入要求比用户名简单的多。如果在社交场景中已经使用了用户名,那需要针对不同类型的用户分别处理。
对于新注册用户,可以在注册流程中用“设置昵称”来代替“设置用户名”,不接触到“用户名”的概念;对于没有昵称的老用户,可以将老用户的昵称批量设置成他的用户名,保证老用户在社交场合中的自我认知没有变化。

对于新用户,提供设置昵称
密码真的重要吗
说完了用户标识,我们来说一说登录密码。但在这里,我希望提出一个“冲击性”的观点:最好不要使用密码。
猛一看,这确实难以接受,因为这不符合现在的主流做法。对于一个比较老的帐号体系来说,这样做意味着更多的成本(你要同时考虑有密码和没密码的用户),但如果要开发新的帐号系统,你应该认真的考虑,是否采用这条作为帐号系统的设计原则。
先来讨论一下密码的使用场景:早在多年前的PC互联网时代,人们就使用登录密码来保证帐号的安全。PC环境和场景比较复杂,人们就围绕登录密码延伸了很多功能:
-
记住密码。过去PC的使用很多在网吧这样的公共场合,为了区分公用和私人场合,会有记住密码的功能(比如QQ)。
-
在公共场合不记住密码,每次必须就要通过帐号+密码进行登录。
-
密码组成的规则限制。当时密码几乎是唯一保证帐号安全的屏障,为了防止穷举的攻击,目前用数字+字母+大写这样组合规则限制密码组成。(近日最初提出这种规则的设计者已经后悔了https://gizmodo.com/the-guy-who-invented-those-annoying-password-rules-now-1797643987)
来到移动时代,手机已经成为了互联网的主要终端。和PC相比,移动端的使用场景已经发生了剧变:
-
移动设备是私人物品,就像自己身体的延伸,没有人会把自己的手机与人共享使用。
-
基于这样的理由,一旦登录成功,只要不换手机,用户几乎不需要再次输入登录密码。
因此,密码以及相关的功能设计,开始变得不再适用于移动场景。
用手机号而不是邮箱作为标识,“不用密码登录”才成为可能。不使用密码,我们用什么登录?之前我们提到,使用手机号标识用户的唯一性,自然可以用手机号+短信验证码的形式登录。这种形式早已经被用户所接受,在用户体验上不存在任何问题。
安全性上呢?这里才是讨论的重点,因为密码不会使你的帐号体系变的更加安全。假设我们仍然让用户设置登录密码,那自然而然会去设计一套完整的密码恢复流程:用手机号+短信验证码找回密码。如果是这样,帐号密码的安全性其实完全依赖于手机号码本身,和直接使用手机号+短信验证码 找回没有任何的区别。
此外,在工程上,由于你保存了用户的登录密码,对于如此敏感的数据,你一定会花很多成本在密码存储、相关流程上做更多的保护。但是做得越多,不代表保护的越好。相反,如果没有专业安全人员的指导,你很有可能会在密码保护的工作上犯错,让攻击者有可乘之机。近些年,我们经常能提到大型网站密码被撞库、脱库的消息,都是攻击者利用密码数据库相关安全漏洞实现的。
有人会问,如果用户手机号更换了,没有密码岂不是无法登录?这是一个现实存在的问题,但是我们也可以预见,这些用户的占比非常低(沪江这样“高龄”的帐号体系,一周也只有个位数的反馈),对于这样的情况,一方面,我们结合风控系统设计一套自助流程去帮助用户解决问题,另一方面让运营或者客服捎带处理一下零星出现的手机更换问题,就足够应付了。
定期修改密码,有没有用?
我们能看到一些熟悉的产品,如 Outlook,Apple ID,都有让用户定期重制密码的机制。毫无疑问,这样的设计的目的是为了保障用户帐号的安全,然而,是否值得?
强迫用户定期修改密码,至少对大部分用户来说是糟糕的体验,起码我没有见过欣然愿意配合定期修改密码的用户。不符合用户的预期,就会导致用户的流失。你为了保障用户帐号的安全,却承受了用户流失的风险。
想出并记住一个新密码需要成本。有些“聪明”的用户会想办法使用原密码,为了规避这样的问题,你一定会存储历史密码记录并阻止用户这样做。这么做只是花了成本在给用户添麻烦,而没有聚焦在核心业务目标与对抗攻击者上。
但是,国内屡次发生大型网站帐号数据库的泄漏事件,会间接泄漏用户在其他网站的密码(用户往往在不同的网站使用相同的帐号名和密码),修改密码机制在这里能发挥一定的作用。然而定期修改密码确实有它不可避免的缺点,所以是否采用它,需要自己考虑它给你带来的价值(避免密码泄露)是否大于它的风险(用户流失与维护成本)。
“鸡肋”的密保问题
密保问题在PC时代是常用的找回密码的方式,至今,一些大厂仍然会使用密保问题作为找回密码的方式之一。但这种做法其实并不明智。
沪江在两年前就取消了密保问题,原因是极低的使用率(0.x%)和相对高的维护成本。去年,在一个与用户资金相关的项目上,我们一度想恢复密保问题,但经过调研举证后,我们还是决定不采用它。其中很重要的论据,来源于谷歌对自己密保问题系统的数据报告:
密保问题的核心问题,是它很难在用户体验与安全之间找到平衡。我们来看一看谷歌报告中的数据:
-
20% 的用户会在“你最喜欢的食物是什么?”这个问题中使用 pizza 作为答案,而对于这类问题,攻击者平均猜 10 次就能将密保问题猜出来。这种频度的尝试基本无法区分是攻击者还是帐号拥有者本人在尝试找回帐号。
-
对于特定地区或语言的人群来说,这种情况更加严重(韩语用户中,高达 43% 的用户对“你住在什么城市”选择的答案是“首尔”,类似的,中文互联网用户也集中在几个一线大城市)。
-
如果是更难猜测的密保问题,对于攻击者来说,难度确实显著增加了。但是对于用户来说,难度也增长了:通过密保问题,用户成功找回帐号的概率只有 40%,相对的,短信或邮件认证的找回成功率高达 80%。
所以,谷歌逐渐放弃了密保问题,转而使用手机短信认证作为帐号找回的主要方式之一。
短信认证没有想的这么简单
既然我们开始使用手机认证作为主要认证方式之一,我们应该保证短信认证服务的稳定。
首先短信通道本身就不是100%可靠的。绝大部分情况下,服务代理商(你很难直接对接到三大运营商)提供的验证码只有95%左右的到达率和及时性。那意味着有5%的用户流失,而这本来是可以避免的。因此我们需要有一个备选的验证渠道:语音验证码。这是一个很好的选择,沪江现在已经把短信+语音作为手机认证的标配,并且做了一系列深入的工作,保障通道的可靠性。

使用语音验证码作为备选渠道
除了短信认证,还有一些产品使用 App 内认证的方式(比如谷歌的身份验证器,暴雪战网通行证的将军令),它拥有比短信认证更高的安全性。至于对短信认证的安全风险感兴趣的同学可以查看(https://zhuanlan.zhihu.com/p/24811129)进行了解。

谷歌身份验证器
然而,我并不是推荐大家都使用所有的安全认证服务。你需要谨慎的考虑,你实施方案所花费的资源,是否值得在你保护的东西上,这本质上是一个性价比的问题。
不要滥用图片验证码
说起图片验证码,大家第一印象可能就是12306“变态”的图片验证码。图片验证码应用于甄别人机有将近20年的历史,到如今也仍然在发挥价值。但是并不是所有的场景都适用图片验证码,比如注册帐号。
有人说,我难道不可以使用图片验证码预防机器大批量注册吗?现在玩法已经完全“变了”,对于批量注册,国内外都已经有非常成熟 & 低成本的打码平台去解决图片验证码的问题。说白了就是把图片验证码分发给真人去鉴别,那图片验证码就瞬间失去了它的意义。

某打码平台价目表
黑产批量注册的动机在于有利可图(比如新用户返利等运营活动),你完全可以在这种场景下用更多的手段进行认证和限制,比如短信验证码或者语音验证码。
“登录失效”并不正常
我们都知道,帐号+密码登录只是一次性的授权,它会通过这次授权从帐号系统拿到一个“令牌”(下文称之为 Token )。后续绝大部分业务流程中的身份认证都是通过这个 Token 去完成的,可见 Token 有着非常大的权利。
所以,自然就会有人想:Token 的权力这么大,出于安全考虑,必须得对它做些限制。于是就出现了有效期,让token定时过期。在终端产品的表现上,每过一段时间,用户的帐号就会自动退出登录,用户需要重新输入帐号密码进行登录。
你不应该让 Token 自动过期。首先它并不能提高帐号系统安全性。要知道,攻击者如果对你的 Token 感兴趣,他往往在一天甚至短短几个小时之内就完成攻击,把 Token 的有效期设置成几十天没有任何意义。
Token失效机制是“背后的逻辑”,用户并不知道,也不符合用户的预期。对于用户来说,他们会逐渐接受这种情况,并认为登录失效没什么大不了的,那某天他们的帐号真的出现了安全问题,他们可能根本无法意识到。
这样的机制设计对于产品流程设计也是极大的负担(产品经理:终于不是我背锅了),因为Token 会失效,你永远不知道用户在哪个业务操作时,Token 就失效了,你需要花精力在这些流程上设计 Token 失效时的产品方案并实现。
根据我们的实际经验,Token 自动过期机制难以维护和排查问题。我们曾经不定期地收到用户反馈,说他们每次登录,第二天登录态就失效。我们尝试排查,却总是找不到原因,原因是这些问题都是随机出现,并且难以复现,几乎无法定位问题。
登出的正确“姿势”
既然有登录,那就有登出。这里也和上一节提到的 Token 有关。据我了解,很多产品在处理用户的主动登出行为时,仅仅是将 Token 从客户端本地删除了,并没有通知服务端。这样处理的风险是巨大的,因为攻击者如果拿到登出的 Token,就可以直接拿 Token 冒充用户的身份在你的网站里做任何事情。这就好像,敌人对你说自己已经解除武装投降了,没有把武器都给你,回过头却给你来一发冷枪,没有任何防备。
正确的做法,当然是客户端删除 Token 的同时通知帐号系统的服务器,服务端同时注销登录状态。后端可以维护这样一个无效 Token 的列表,成本较低的做法也可以直接删除。
未来的帐号与安全
我们说了很多帐号系统的设计原则,绝大部分都与帐号安全有关。但是值得一提的是在安全领域,“安全”并不是绝对的概念,世界上没有攻不破的盾牌,攻击和防御只是资源比拼的游戏,攻击者只要付出足够的代价一定能攻破任何一个防御系统。我们设计系统的安全机制,一定取决于不同的用户场景对安全的需求:
未来的帐号安全体系,一定是多层次的。刚才我们提到,攻防是资源比拼的游戏。需要保护的信息重要一些,那么安全机制就强一些;需要保护的信息不那么重要,安全机制就可以弱一些。因此对于不同的场景有不同的帐号认证策略是合理的,这就是多层次的安全体系。比如,对于普通的登录场景,可信设备+简单登录验证就可以通过身份鉴权;但对于支付场景这样对安全需求更高的环境,还需要加上加密传输+安全控件等更多的限制。而随着指纹&人脸识别等更多生物识别技术的发展,我们可以预见到这些技术也能够应用到更多不同场景下的帐号安全 & 认证策略。
未来的帐号安全体系,也一定是个性化的。多层次的安全体系也意味着它能够满足用户不同安全等级的需求。一个用户,他可以认为他的谷歌帐号对他很重要,对它有更高的安全要求,那他可以启用 Google 二步验证(https://www.google.com/intl/zh-CN/landing/2step)来提供安全性;他如果认为微博帐号就不那么重要,安全要求就低一些,使用简单的密码这样基本的认证手段即可。
关于帐号系统的经验和“坑”,其实远不止以上这些。设计一个好的帐号系统,需要不断的钻研和尝试。但请记住,网站/产品的核心目的,不是设计一个“牛逼”的帐号体系,而是达成核心的业务目标。你设计的帐号体系需要适合你产品的业务形态,并且具备未来的拓展性。希望大家能通过这篇文章找到灵感,设计出适合自己产品的帐号系统。


