当前位置: 首页 > news >正文

Node.js躬行记(32)——F2A实战

F2A(Two-Factor Authentication)其实是2FA的笔误,都表示双因素认证。它是一种安全验证机制,要求用户在登录时提供两种不同类型的证据(因素)来证明身份。

在实际应用中,F2A/2FA 最常见的表现形式是:

  1. 密码 + 短信验证码:最普遍但安全性相对较低(易受 SIM 卡交换攻击)。
  2. 密码 + 基于时间的一次性密码( the Time-Based One-time Password ,TOTP):如 Google Authenticator、Microsoft Authenticator 等应用生成的 6-8 位动态数字。
  3. 密码 + 硬件密钥:如 YubiKey,安全性最高,能有效防御网络钓鱼。

我们的后台将会采用第二种表现形式,后台服务端基于 Node.js 实现。核心实现原理是基于时间同步的一次性密码(TOTP,Time-based One-Time Password)算法,通过“共享密钥”和“时间”两个要素,在用户设备与服务器之间生成相同的动态验证码。即服务器和你的手机APP,各自使用同一个密钥和当前时间,通过相同的算法计算,得出一串相同的数字。

一、生成密钥

1)speakeasy.js

Node的生态提供了speakeasy.js库,可以生成F2A的密钥,还有供F2A设备扫描的地址。

npm install --save speakeasy

在项目中安装完成后,就是调用 speakeasy 的生成方法。

router.get( '/get/f2a', async (ctx) => { const secret = speakeasy.generateSecret({ length: 20 }); ctx.body = { code: 0, data: { secret: secret.base32, url: secret.otpauth_url, } }; }, );

secret 就是一串字符,而 url 是个 otpauth 协议的地址,生成二维码后便于设备扫描。

{ "secret": "M4STOUBFGM4UUXJXKZJXS5J4IIWEA3KU", "url": "otpauth://totp/SecretKey?secret=M4STOUBFGM4UUXJXKZJXS5J4IIWEA3KU" }

2)账户绑定密钥

点击上图中的重新绑定按钮,就能将当前 secret 和后台账户绑定起来,在账户表中新增两个字段:otpauthUrl 和 secret。

{ "_id": { "$oid": "5f81288d3578bb005a79cdc1" }, "status": 1, "realName": "测试", "userName": "xx@xx.me", "cellphone": "13800138000", "password": "abcd", "otpauthUrl": "otpauth://totp/SecretKey?secret=PU4WG5RDJVZVCSDHMM5UYSBFEMSEANBQ", "secret": "PU4WG5RDJVZVCSDHMM5UYSBFEMSEANBQ" }

后续在登录输入安全码后,就可进行校验了。

二、绑定应用

1)APP

在应用市场提供了很多的APP,用于显示安全码,界面基本上都是下图这样,例如 authy、2FAS 等。

2)小程序

有个叫二次验证码的小程序,也能用于绑定。

3)飞书小助手

还设计了一种更简便的查询方式。

在机器人的对话框,输入安全码,空格后面跟上自己管理后台的账号邮箱,例如“安全码 xx@xx.com”。

如此,就能得到该账户的最新安全码。

原理就是查表,然后调用 speakeasy 的 totp() 方法,再调用飞书的接口发送消息。

const account = await this.models.BackendUserAccount.findOne({ userName: email }); const token = speakeasy.totp({ secret: account.secret, encoding: 'base32', }); // 发送回复消息 await this.messageService.sendTextMessage(chat_id, token);

不过还有个小问题,就是也能收到别人的安全码。但后台都有查询记录,若出现问题,还能溯源。

三、F2A校验

1)开关

增加一步登录校验,在使用时会增加门槛,所以在通用配置中设计了一个开关。

{ "isOpen": false, "whiteList": [ "yy@yy.com" ] }

只有在 isOpen 为 true 时,才会开启校验。

whiteList 是给特殊账户开启的白名单,不需要走校验,例如给第三方用的账户。

2)登录

登录设计成了两步,两次调用的接口都是 user/login。

第一步还是原先的输入邮箱和密码。

输入完后,去请求登录接口,判断是否需要二次校验,若返回JSON包含 f2a。

{ f2a: true }

则显示弹框,安全码为必填项,点击确定进行二次校验。

下面是登录接口中的部分逻辑,从通用配置中读取开关信息,判断是否弹框,最后校验。

// 判断是否需要F2A安全校验 const configContent = await services.tool.getConfigContent({ key: '1a26f4185f66d6f94ef3897f7a475305' }); if(configContent && configContent.isOpen && configContent.whiteList.indexOf(userName) === -1) { // 未传随机码,说明是第一步校验,直接返回 if(!code) { ctx.body = { f2a: true }; return; } // 校验随机码 const verifyInfo = { secret: account.secret, encoding: 'base32', token: code, } const verify = speakeasy.totp.verify(verifyInfo); if(!verify) { ctx.status = 400; ctx.body = { error: '安全码错误' }; return; } }

3)过渡期

在开启这个功能前,会有一段时间的过渡期,在登录页面增加说明文档。

http://www.jsqmd.com/news/1071892/

相关文章:

  • YimMenu终极指南:GTA5安全增强与游戏体验优化完整教程
  • VutronMusic:跨平台音乐播放器终极指南 - 免费开源的高颜值第三方网易云播放器
  • 《开源友的聊》第三期直播预告|当大模型成为基础设施,开源还能怎么赚钱?
  • 【AI自动化生产力革命】:20年运维专家亲授5大AI工具+批处理无缝整合实战秘法
  • 损失函数 的 硬截断 和 平滑衰减
  • 如何高效解决浏览器全屏API兼容性问题:screenfull.js进阶实战指南
  • Get Shit Done:重新定义AI编程工作流的革命性框架
  • 拒绝踩坑!企业搭建多商户商城/知识付费平台,技术选型到底该看什么?
  • 全能免费在线工具箱ToolBoxMax,100+工具本地浏览器运行,保护隐私无需注册
  • 杭州吟颂职称政策调研:浙江省工程师申报要求
  • 双重检测不用慌!okbiye 分层降重降 AIGC 方案一次性打通论文审核关卡
  • 深度解析kohya_ss训练监控:5个关键技术指标与可视化实战指南
  • 为什么 SSR 一定会有 hydration mismatch?
  • 3步轻松上手ESP32物联网开发:Arduino核心的终极入门指南
  • 正态总体样本方差、t 分布 纯文本笔记
  • Git 超详细入门教程(附实战命令 常见坑)
  • 【影刀】手机自动化运行输入框无法输入文字,报错提示ACTION_SET_PROGRESS has failed on the element ‘android.view.accessibility.
  • 5个PDFPatcher实战技巧:免费解决PDF格式难题的完整指南
  • 流式微调(Streaming Fine-tuning)正在重构AI架构——3家头部企业已验证的4类低代码集成范式
  • PDFPatcher完全指南:5个实战技巧快速解决PDF处理难题
  • 终极指南:如何让老旧Mac免费安装最新macOS系统
  • 【昇腾/AscendC开发】AscendC 910B GM 标量/MTE 双向缓存不一致 Bug 详解
  • PREEMPT_RT 技术实现:local_lock
  • PDF补丁丁完全指南:5个免费开源技巧彻底解决PDF编辑难题
  • 如何让Intel显卡火力全开:MPV播放器硬件加速终极优化指南
  • 试试连Claude Code团队都在使用的终端软件Ghostty
  • PDF处理架构解析:PDFPatcher开源工具箱的技术实现与实战指南
  • 物联网智能锁实战:公寓/集团宿舍实名核验+远程授权落地方案
  • 太原食品级干冰
  • ESP32 Arduino开发终极指南:5步轻松配置物联网开发环境