最难不过二叉树

身份认证技术之2FA、TOTP、WebAuthn

2024-08-09

大家有时候会混淆认证和授权两者的在系统的里的作用,他们的职责是这样的:认证(Authencitation)解决了系统回答用户“你是谁”的问题,而授权(Authorization)回答了用户“你能干什么”的问题。“用户登录”这个最基本的操作就是一个身份认证的问题,通过系统认证之后,用户对系统发起的请求都会被解读为某用户的操作请求,即请求跟用户身份绑定了。本文将聊一聊要设计一个安全性较高的系统,在普通的账密登录的基础上,我们还能做些什么来加固架构安全性。

我们从最简单的账密登录说起。最基础的登录系统要求用户输入账户和密码两个信息进行登录认证,账密这种认证方式,从各个方面看来就是不安全的。比如:

  1. 你的密码是不是有可能被其他人偷看到而泄露?
  2. 你的客户端是不是有可能被植入病毒,进而窃取了你的密码输入?
  3. 你的密码在网络传输中是不是有可能被抓包进而窃取到?
  4. APP的服务器被拖库后,你的密码是不是也泄露了?
  5. 如果你的密码在各个APP上都一样,某一个APP被拖库后,你的另一个APP的密码是不是一样也泄露了?

从上面的场景分析下来,34点可以通过客户端和服务端加密配合和HTTPS来规避风险,125点这个就不好防范了,这个大多数是因为用户个人问题导致的密码泄露。这里就可以下一个结论:账密登录的认证方式,本身就是不安全的,不管如何优化这个认证过程,总会有密码泄露的可能。

2FA

所以我们做认证方案设计时,需要在账密方案的基础上,加多其他身份验证因素,这个认证方法业界称为MFA(Multi-factor authentication),即多因素认证。如果只有2个认证因素,那就是2FA(Two-Factor Authentication),双因素认证。

这里提到了认证因素的概念,认证因素可以分为以下几类:

  • 知识因素:用户知道的事情。例如用户名及密码、安全问题的答案等。
  • 拥有因素:用户拥有的东西。例如钥匙、独立的设备、安全令牌、身份证、提款卡等等。
  • 固有因素:作为个人的用户所独有的东西。比如生物特征:例如人脸识别、指纹识别、语音识别。
  • 位置因素: 通常基于用户尝试验证其身份时的所在位置。组织可以限制位于特定位置的特定设备进行身份验证尝试,具体取决于员工登录到其系统的方式和位置。
  • 时间因素: 此因素将身份验证请求限制在特定时间内,只有在此时间内用户才能登录到服务。此时间之外的所有访问尝试将被阻止或限制。

知识因素是用得最广泛的身份认证因素,但是只要黑客足够了解用户,就能破解某些安全问题的答案,甚至密码也能在较短时间内碰撞出来。与知识因素相比,拥有因素有几个优势:比如手机短信这个因素,由于接收短信和运行应用的载体分属两个不同的网络,因此黑客需要必须同时截取到两个不同通信信道才能获取到凭证,这将大大增加了黑客窃取信息的难度。但是拥有因素一样有风险,因为你拥有的实物有可能被盗或者丢失,可能也被黑客获取到和克隆,比如你收到的认证短信可能会被身边的人所看到。固有因素是最难破解的因素,它们不会被遗忘、不会丢失或放错地方,而且很难复制。位置因素和时间因素只在特定场景下可以采用,受限的较多,这里不做过多介绍。互联网上,用得最多的还是知识因素,拥有因素和固有因素这三种因素。

在实际互联网业务中,如果认证因素太多的话,将极大地降低用户使用产品的欲望。用户认为,用户的密码保护是互联网企业应该做的事情,而不应用户参与太多。如果你的产品在用户注册环节,既要求用户填写密码,又要求用户填写邮箱、手机号,还要求绑定个硬件动态令牌,这样的产品我估计很多用户都不愿意试用。因此在产品设计中,产品经理很可能就不允许使用多因素认证。不过现在很多的产品,双因素认证倒是用上了,这个方式在安全性和用户体验上有一个不错的平衡。至于更多因素的认证,可能只存在于保密级别很高的项目,比如军事机密等。

举一些熟悉的场景帮助理解,这些场景都用到了2FA手段来加强认证过程:

  • 使用手机银行转账时,除了要求输入支付密码外,还得输入手机收到的支付短信验证码,二者具备通过后才能完成转账。
  • 登录Steam游戏平台时,使用手机令牌或者邮箱验证码进行验证。

用户登录应用程序、服务、网络设备系统时,2FA认证流程原理都相同。下面以应用程序的2FA认证流程为例:

  1. 用户登录应用程序。
  2. 用户输入登录凭证,通常是账号和密码,做初始身份验证。
  3. 验证成功后,然后将提示用户提交第二个身份验证因子。
  4. 用户将第二个身份验证因子输入至应用程序,如果第二个身份验证因子通过,用户将通过身份验证并被授予对应的系统操作权限。

这里第二个身份验证因子用得比较多的是手机短信和硬件令牌(动态口令)。手机短信其实用得最多,但是短信的传输同样要经过网络,当信息在信道上传输时,就有被截取的风险。那怎么降低这个风险?那就避免敏感信息在网络的传输,这里就得提到我们的硬件令牌了。硬件令牌其实用得也挺多的,个人用到的场景有:

  • 腾讯员工入职时就会发一个令牌,实时生成动态口令,要连接公司内网或者登录跳板机,就得输入这个动态口令。
  • 玩梦幻西游时,账号需要绑定将军令,将军令本质就是个硬件令牌,每次登录游戏都要输入动态口令。
  • 网易游戏员工登录VPN时,也需要输入动态口令,但这里的硬件令牌不是其他物品,而是自己的手机。

image (27).png

TOTP

这种硬件令牌生成动态口令的认证方式称为TOTP,全名是 Time-based One Time Password,顾名思义,使用目前的时间来生成一次性密码,因此天生就具有随机性。

TOTP有两个特点:

  • 一次性:只能认证一次,如果认证错误了,只能等刷新下一个口令来认证
  • 基于时间生成:生成的口令跟当前时间相关联

TOTP 认证流程如下:

  1. server生成带有secret key的QRCode。第一步,产品页面会生成一个二维码页面,我们用手机扫一扫后会得到server给你分发的secret key。比如secret=X5CTBOMEYE3TXIIS

image (28).png

2)将secret存入手机的Authentication App(我用的是Microsoft Authenticator ),后续生成的动态口令就是根据该secret和当前时间戳计算得到的。假设当前timestamp=1654994758,假设APP希望每30秒就会刷新一次口令,此时hash = 十进制(HMAC(timestamp/30, secret)),因为我们的动态口令是6位数,因此取hash最后6位就是我们生成的动态口令。如果动态口令是4位的,那就取hash的最后4位。

3)这里动态变化的是timestamp,当30秒过去之后,我们再取最新的timestamp进行计算,又能得到完全不一样的6位动态口令。

generatedcode.png

  1. 有了这6位口令后,在30秒内输入到产品登录页面,如server算的口令跟你一样时(server那边执行一样的计算步骤,一样的参数),那么你的登录认证就可以通过了。

image (29).png

这里分析一下TOTP的安全性。首先,相对于手机验证码或者邮箱验证码而言,TOTP没有走通信网络,因此口令的校验过程没有被窃听的风险,因此更安全。TOTP会不会被碰撞出来?因为口令有6位,6位数字有一百万种可能,30秒内碰撞出来口令要求太高了,而且这口令认证错了一次就会失效,等下一个30秒后才能继续验证,所以暴力破解是不可能的了。

TOTP有个问题得注意一下,那就是时钟同步的问题。客户端和服务端计算口令时都使用到了当前时间的timestamp,假设客户端时间和服务端时间相差超过30秒,那是不是就有问题了?如果你的硬件令牌是通网的,时间可以做矫正和同步。如果你的硬件令牌不通网(比如将军令),那怎么处理这个问题呢,因为时钟偏差一定会出现的。

服务端计算动态口令时,可以计算出当前时间下前后2个,共计5个(t-60, t-30, t, t+30, t+60)TOTP,只要传入的TOTP在这5个中,也算证成功。当然此时服务端还可以将时间偏移量存下来,比如这次客户端算的口令跟服务端t+30算的口令一致,那么可以认为客户端已经有了t2=30s的时间偏移,那么下次计算临近4个的口令时,取的时间就是t+t2-60, t+t2-30, t+t2, t+t2+30, t+t2+60 这五组时间戳。

但是你需要考虑另外的问题:这个secret保存在哪里?现在当然是放在了Authentication App里,那这个安全吗?只要你的手机不被破解,你的Authentication App里的secret就是安全的。

如果我们的硬件令牌丢失了怎么办,比如Authentication App所在的手机丢失了,我们还能怎么登录到产品?必须强度的是,一旦丢失了这个物理令牌,后续要恢复账号将十分困难。这里介绍一下Github是怎么处理丢失2FA凭据时恢复账户的(假设只使用了TOTP作为你的第二验证因素):

有3种方法可以帮助恢复账号:

  1. 如果之前已经使用当前设备登录此帐户,可以使用该设备进行验证,前提是该设置还保留着Cookie 记录。
  2. 如果之前已在此帐户上设置 SSH 密钥,可以使用此 SSH 密钥进行验证。
  3. 如果之前已设置 personal access token,可以使用 personal access token 进行验证。

如果以上三种方法你都不具备,那么你的账号就会永远丢失了。如果2FA的第二个因素你选择的是手机验证码,如果你的手机丢失了,你还可以挂失你的手机号,你的手机号是不会丢失的,所以你的APP账号不会有丢失的风险。而TOTP的方式,一旦手机丢失了,那么麻烦可就很大了。

WebAuthn

2FA第一步还是要认证用户的密码,只要有输入密码这一步,你的密码就有可能被窃取,因此要想更安全的做用户认证,最好还是没有所谓的账密输入这一步。WebAuthn(全称 Web Authentication)抛弃了传统的密码登录的方式,而使用生物识别(指纹、人脸、虹膜、声纹)作为身份凭证,从根本消除了用户输入等风险和繁琐步骤,做到真正的无密码身份认证。

WebAuthn 的优势:

  • 安全性更高:不再依赖密码的依赖,减少网络钓鱼、暴力破解和重放攻击的风险。
  • 提升用户体验:提供更简单、更快速的登录,通常只需一次点击或生物识别验证即可登录。
  • 隐私保护:在身份验证过程中不会传输共享秘密内容,个别网站也不会接收到任何个人身份信息。

WebAuthn工作原理,这里假设用户的验证器和应用终端都是手机。

用户注册时:

  1. 手机向服务器发起注册请求chanllenge,服务器回复chanllenge。chanllenge其实就是一个随机字符串。
  2. 手机进行生物认证(指纹、人脸等),通过后在本地生成私钥和公钥,用私钥对challenge进行签名,将自己的公钥、签名发给服务端。
  3. 服务端接收公钥后,用公钥解密传上来的签名,验签成功后,把公钥存储到数据库,此时注册成功。

2-Registration.svg

用户登录时:

  1. 手机填入用户名,点击登录,向服务端请求challenge。
  2. 服务端返回challenge。
  3. 手机进行生物认证,通过后将challenge和本地私钥做签名,计算好的签名发送给服务端
  4. 服务端从数据库中找出账号对应的公钥,对数据进行验签。公钥解密成功,说明登录成功,解密失败说明登录失败。

3-Login.svg

WebAuthn采用非对称加密的公钥、私钥代替传统的密码,私钥是保密的,只有验证器知道它,连用户本人都不需要知道,也就没有泄露的可能。公钥是公开的,服务器可以通过注册时的公钥就能解密判断用户的身份是否合法。更重要的是WebAuthn在保证安全的前提下带来了巨大的便捷性,不仅在注册和登录体验上十分便捷,而且彻底避免了用户在一个网站泄露密码,所有使用相同密码的网站都会受到牵连的问题,这个优点使得用户无需再为每个网站想不同的密码。

但是,WebAuthn有个缺点是致命的,那就是跨平台限制和跨设备限制。

在 Android 设备上使用基于 iCloud 的密码极具挑战性。比如你之前使用的是IOS手机,使用WebAuthn注册了一些网站的账户,此时我换了Android的手机,此时我的这些网站账号还能怎么登录呢?正因为秘钥串无法实现跨系统(Apple-Android),所以使用WebAuthn时要考虑好跨系统的问题。

另外跨设备可能也存在问题,苹果系列因为有iCloud所以不存在秘钥不能多设备同步的问题,但是其他系统的设备就不一定了。比如windows笔记本的秘钥,我更换到另一台笔记本时,能否也能同步这个秘钥呢?由于私钥绑定本地,且没有开通云同步,这会使跨设备操作变得困难。