用 Telegram Bot 实现使用 OpenPGP 登陆的两种方式

其实做这个一方面是为了想入门 Telegram Bot 开发,一方面是想另外拓展一下 OpenPGP 的使用范围

Telegram Bot

Telegram Bot 是开放注册的,并且拥有许多封装好的 API 供给开发者使用

注册一个 Bot 需要向 Telegram 的 BotFather 注册

注册好后会返回给你一个 Bot 的 Token

Telegraf

Telegraf 是一个 Node.JS 上的 Telegram API 封装

通过 Telegraf 提供的构造函数来初始化一个 Bot

1
2
const { Telegraf } = require('telegraf');
const bot = new Telegraf(TOKEN);

接下来可以试试

1
2
3
4
5
bot.start(ctx => {
ctx.reply('Yo');
});

bot.launch();

启动后当 Bot 接收到第一次使用 Bot 时的 /start 指令就会返回给你一个 Yo

OpenPGP 登录的两种方式

  1. 生成一次性的登录 TOKEN 给用户并且要求使用用户的私钥签名,签名后把签名好的内容返回给 Bot,Bot 使用用户公钥进行签名验证;验证通过需要满足两个条件,签名必须可以被该用户的公钥验证为合法签名,签名内容必须与一次性登录 TOKEN 相符

  2. 生成一次性登录 TOKEN,通过用户的公钥加密后发送给用户,用户使用私钥解密拿到 TOKEN,再传送给 Bot 进行登录

这么做的好处是服务器并不需要存储用户的密码和其他敏感的登录凭证信息

即使发生了脱裤,攻击者只能拿到用户的公钥

用户只需要保管好自己的私钥,其他的账户就安全无忧

主要实现

Bot 会在用户 /start 后,来检测在 Firebase 的 Firestore 中的 Keys 集合中是否存在使用 Telegram ChatID 命名文档,并且文档中存储有用户的公钥

如果没有,会提示用户使用 /reg 来上传自己的公钥

上传之后会在 Firestore 中的 Keys 集合通过用户的 ID 建立一个文档来存储该公钥

当用户需要验证的时候,使用 /getcode 命令

例如使用签名登录,会随机生成一个字符串并存储到 login 集合下用用户 ID 建立的文档中,并且该随机字符串也会发送给用户

用户只需要用自己的私钥签名,然后通过 /login 的命令发送给 Bot

Bot 会向 Firestore 中 login 集合中该用户 ID 的文档索取随机字符串,并且也会向 Keys 集合中获取到用户的公钥,然后用 OpenPGP.JS 的 Verify 方法来对签名进行验证

验证后 Verify 返回的对象中会包含 Valid 的合法值 和 verifyed.data 的签名数据

接下来只需要做一个简单理解的判断即可 verifyed.data === code && valid

两个都为 True 即可判断为是成功

如果使用解密的方式登录,还是一样输入 /getcode 并且在后端存储随机字符

下一步操作就是从 Keys 集合中获取公钥,并且用 OpenPGP.JS 的 Encrypt 方法来对随机字符加密

然后把加密的内容传送给用户

验证也是同理,用户输入的跟 Firestore 保存的一致就行

当用户通过两个任意一个验证,建议将随机生成出来的 TOKEN 移除