机器人

机器人安全与防抓取:速率限制、DDoS 与令牌保管

公开机器人意味着任何人都能向它发送输入。本文讲解如何验证投递来源、用数字标识符而非用户名识别发送者、构建收发两端的速率限制、降低令牌泄露的代价,以及通过设计而非检测来防止数据被抓取。

内容导读

公开机器人意味着任何人都能向它发送输入。本文讲解如何验证投递来源、用数字标识符而非用户名识别发送者、构建收发两端的速率限制、降低令牌泄露的代价,以及通过设计而非检测来防止数据被抓取。

机器人安全与防抓取:速率限制、DDoS 与令牌保管主题插画

公开机器人是一个世界上任何人都可以永久、免费向其发送输入的程序。这就是你出发时的全部安全模型,而大多数真正重要的防御都源于认真对待这一点。

有三样东西值得保护,而它们的重要性并不相同。令牌、数据,以及你持续发送的能力——按此顺序,因为第一样会让你失去一切,而最后一样只是令人烦恼。

完成这一步后你将能够做到

  • 说明验证一次投递证明了什么,以及它没有证明什么。
  • 正确地识别发送者,而不是依据他们自己选择的东西。
  • 构建速率限制中人们通常跳过的那一半。
  • 降低一次泄露实际造成的损失。

你的 webhook 在每次投递时都会检查共享密钥。那么其中的数据现在就可信了吗?

是的——经过验证的投递意味着内容来自 Telegram

不——它证明的是谁投递了它,而不是谁写了里面的内容

是的,前提是你还按网络地址进行限制

这个密钥只回答一个问题:这个请求是来自 Telegram,还是来自某个发现了你地址的人。它对内容只字未提,因为内容就是某个陌生人输入到你机器人里的任何东西——显示名称、消息正文、文件名、说明文字、他们看到的按钮上的文字。这些都是来自开放互联网的用户输入,而它们最终会进入你的日志、数据库、模板,很可能还有管理员的屏幕。像对待匿名访客提交的表单那样对待一次更新,因为它恰恰就是这样的东西。

一条消息带着你同事一模一样的名字和头像照片到达,要求机器人执行一条管理命令。机器人应该检查什么?

将显示名称与员工姓名列表进行比对

用户名,它是唯一的且无法被重复

数字账户标识符,发送者无法自行选择

名字和照片可以随意复制,几秒钟就能设置好,这就是第 065 步中整个冒充问题的全部。用户名更好,因为它们是唯一的——但它们可以被释放并被别人占用,而只差一个字符的相似用户名是一种老套的伎俩。数字标识符是被分配的而非自选的,不会改变,也是授权应当依据的东西。这不是理论:一个基于名字授予权限的机器人,就是一个向任何设置该名字的人授予权限的机器人,而这次尝试对攻击者毫无成本。

你的令牌在一个公开仓库里放了六个月。似乎没有发生什么坏事。现在该怎么办?

什么都不用做——如果它被滥用了,你早就该注意到了

撤销它,并假定它能读取的一切都已被读取

在下一次发布时轮换它,因为目前没有什么紧急情况

“没有可见的事情发生”正是最有价值的那类滥用的预期表现。令牌让持有者能够接收你机器人接收到的内容,而读取不会留下任何你能看到的痕迹——没有发送消息、没有更改帖子、没有任何异常。公开代码正是被自动且持续地扫描这类字符串的,所以现实的假设是它已经被发现了。现在就撤销它(第 086 步),弄清楚在那段时间窗口内它能触及什么,并把该期间经过机器人的任何敏感内容视为已被看到。

端点,以及值得拥有的那道门

如果你的机器人是被调用而非主动询问(第 091 步),它的地址就在公开互联网上,会收到来自非 Telegram 用户的流量。两道防御,按顺序来。

共享密钥。Telegram 可以在每次投递时附带一个你选择的值。在解析任何内容之前先检查它,拒绝未通过的,并以不泄露时序的方式进行比较。它成本低廉,正是为此设计的,能彻底消除伪造的投递。

无论如何都把内容当作敌意对待,原因见第一个测验题。

按网络地址过滤有时被建议作为第三道门。它可能有帮助,但有一点值得说明:已公布的地址范围会变化。配置文件里硬编码的列表就是一场没有明显原因的、未来的故障,所以如果你这么做,要从已公布的来源自动拉取这些范围,并且绝不要让它成为互联网与你的处理程序之间唯一的屏障。

速率限制有两半

人们构建了进来的那一半,然后在一次事故中发现了出去的那一半。

进来的:一个人能让你做什么。有人按住一个命令不放并不是什么高明的攻击;那只是一个无聊的用户或一个出故障的客户端。在你做任何工作之前施加的按账号限制,能防止它消耗你的数据库连接,或消耗你调用任何外部服务的预算。注意它做不到什么:它不能阻止他们发送。他们的消息仍然会到达;你只是选择不对其采取行动。

出去的:你能让 Telegram 做什么。这才是真正咬人的那一半。发送是有节奏的,无论是总体上还是特别是发往单个聊天,而一个向列表广播、在循环中回复或淹没错误频道的机器人会被要求等待。尊重这个等待。一个无视它的重试循环会把短暂的暂停变成漫长的暂停,而这种情况的经典版本是:一个错误报告频道在它本应发挥作用的那次事故中停止工作,因为它试图发送四千条一模一样的消息。

对于任何批量发送的东西,实用的形态是一个有刻意节奏的队列、一份记录了向谁发送了什么的记录,以及恢复的能力——这与第 093 步从另一个方向得出的结论相同。

抓取是一个设计问题,而非检测问题

如果你的机器人会回答任何人的任何查询,它就是通往你数据的一个公开接口,最终会有人把它从头到尾走一遍。检测那件事是一场由你出资的军备竞赛;决定什么可以批量获取则是一次性的决定。

有用的问题不是“我如何阻止抓取”,而是“这个机器人交出的东西被完整复制一份会值多少,对谁而言?”一个做单位换算的机器人没有什么可被拿走的。一个能按标识符查找任何记录的机器人就是一个带聊天界面的数据库,而人类必须逐条输入查询这一事实并不是防御,因为那个人类可以是一个脚本。

三个设计选择就能覆盖大部分情况。一次只返回一样东西而不是列表。要求请求者必须拥有某样东西才能获取任何敏感内容——一个订单号而非一个电子邮件地址,一个验证码而非一个名字。并且不要暴露一个可以向上计数的标识符,这正是顺序查找变成完整导出的方式。

现在试一试——五分钟

四个步骤,没有一个需要新的基础设施。

  1. 找出你的令牌在哪里,并大声说出来 如果这句话里包含一个你编辑的文件、一张截图,或一条发给同事的消息,那就是今天的任务。
  2. 在你自己的代码里搜索对名字的检查 任何授权依赖于显示名称或用户名的地方,都把它替换为数字标识符。
  3. 给你的机器人发送一些荒谬的东西 一条非常长的消息、一个名字奇怪的文件、充满标记字符的文本。看看你的日志和模板会怎么处理它。
  4. 看看你存储了什么,然后删掉一列 最廉价的安全工作是少持有。第 093 步有论证;这是五分钟版本。

降低一次泄露的价值

这个领域的大部分精力都花在把人挡在外面。更可靠的投资是让内部变得不那么有价值,因为无论挡人是否成功它都有效。

一个用途一个机器人。一个能向你的主频道发帖、管理你的群组并运营你的商店的令牌,离这三者全部泄露只差一次泄露。用独立的令牌和狭窄的权限分开机器人,能把一次全面泄露变成一次受控的泄露,代价只是在 BotFather 里花几分钟。

狭窄的权限,定期复查。第 086 步的规则在这里适用,作为一项持续的习惯而非一次性的设置步骤:那个为一次性清理而被授予删除权限的机器人仍然拥有这些权限,而一个旧频道的管理员列表正是被遗忘的凭据藏身之处。

少存储,并让你存储的东西过期。一份永久保留的对话日志正是让一次入侵变成一则新闻的东西。你从未保留的数据无法被拿走。

按人员轮换,而非按日程。令牌泄露的现实时机是有人离职、笔记本电脑被更换,或合同结束时。把轮换与这些事件挂钩,比一个没人遵守的日历提醒更有价值。

大多数机器人泄露并不高明 它们是为求助而发的截图里的一个令牌、一个被提交到公开仓库的工作文件、一个在项目结束后仍存活的群里分享的凭据,或一个仍能访问部署的前承包商。这些都不是防火墙或速率限制器能解决的,而所有这些都能通过知道你的机密在哪里以及谁能触及它们来解决。

这个阶段的常见问题

有人能找到我的 webhook 地址吗?

假设能。它会出现在部署配置、日志、截图里,而且如果你在一个可解析的域名上可预测地命名它,它就是可猜的。把地址当作机密不是一种策略;验证共享密钥并怀疑内容才是,而且无论有没有人找到它都成立。唯一值得做的是不要把地址放在任何公开的地方,这属于卫生习惯而非防御。

我如何屏蔽一个滥用用户?

维护一份你的机器人忽略的账号列表,并在处理的最开始、做任何工作之前检查它。你无法阻止他们发送——他们的消息会持续到达——但你可以让他们不产生任何成本。在群组里,房间自身的工具是更好的手段:限制或移除他们是一次带有审计轨迹的版主操作,这正是第 059 步存在的意义。

当我触发送送限制时是什么样子?

发送开始失败,并附带一条等待特定时长的指示,而如果你继续尝试,这个时长会增长。正确的处理是停止、等待被告知的时长,然后恢复——这意味着你的发送路径需要能够暂停,而不是一个立即重试的循环。无视这一点的机器人一生都处于被限流的状态,在用户看来就是随机地不可靠。

我应该在机器人前面放一个 CAPTCHA 吗?

对机器人本身几乎从不需要——它给每一个诚实的用户增加摩擦,只为给一个可以花钱雇人破解的攻击者制造不便。挑战真正有价值的地方是群组的门口,正如第 064 步所述,因为在那里被阻止的是批量自动加入,而非某个使用你服务的个人。如果一个机器人正被大量滥用,按账号限制和忽略列表能以对其他人更小的代价做更多的事。

记录完整的更新安全吗?

它很方便,也意味着你的日志文件包含每个人的消息,包括任何误发给你的内容。如果你需要它们来调试,就短暂保留,并与你的普通应用日志分开存放,并遮蔽明显的敏感字段。测试很简单:如果那个日志目录的一份副本出现在某个公开的地方,里面会有什么?诚实地回答通常会改变被记录的内容。

我需要一个机密管理系统吗?

对于一台服务器上的一个机器人,由你的托管方设置的环境变量完全足够,而伸手去够更重的工具是一种让自己感觉安全而非真正安全的方式。重要的是那些属性,而非那个产品:机密不在你的代码里、不在你的仓库里、不在你的镜像里,并且无需更改代码就能更换。当你有很多机密和好几个人时,一个专门的系统才开始物有所值——在那之前,它只是又多了一个要运行的东西。

← 返回 TG知识库