自 PR #43042 引入的更改以来,/u/check_username.json 端点已实施速率限制,每个 IP 地址每分钟最多允许 10 次请求。
这似乎导致了正常注册流程中的回归问题。
在创建新账户时,Discourse 会自动检查用户名的可用性。用户无需反复提交表单即可达到此限制。仅仅是输入电子邮件地址和用户名、在输入过程中暂停、更改用户名或进行更正,都可能导致向 /u/check_username.json 发送多次请求。
一旦达到限制,注册表单将显示以下提示:
您执行此操作的次数过多,请稍后再试。
该错误直接显示在用户名字段下方,并实际上会阻止用户继续操作,直到速率限制器过期为止。
没有任何提示告知用户需要等待多长时间,而且从用户的角度来看,这看起来像是所选的用户名或注册表单出现了故障。
复现步骤
- 以匿名用户身份打开注册表单。
- 输入一个有效的电子邮件地址。
- 多次输入并修改用户名,并在每次修改之间允许用户名可用性检查运行。
- 继续操作,直到
/u/check_username.json在一分钟内被调用超过 10 次。 - 用户名字段开始显示速率限制错误:
您执行此操作的次数过多,请稍后再试。
预期行为
正常完成或更正注册表单的用户不应因自动用户名可用性检查触发的内部速率限制而被阻止。
如果为了防止滥用需要速率限制,正常的注册用户体验应优雅降级,而不是暴露速率限制错误并阻止账户创建。
实际行为
用户在用户名字段上收到内联验证错误,并且无法继续正常操作,直到速率限制过期。
相关更改
这似乎是由以下更改引入的:
PR #43042 – DEV: 按 IP 对 check_username 请求进行速率限制
当前实现使用了:
RateLimiter.new(
current_user,
"check-username-#{request.remote_ip}",
10,
1.minute
).performed!
注册 UI 本身可能会生成多次用户名检查,因此在合法交互期间可能会达到每分钟 10 次请求的限制。
对于位于共享 NAT/公共 IP 地址后的用户,这可能甚至更具问题,因为限制器是按 IP 而不是按会话进行的。
额外观察
check_email 也有一个每分钟每 IP 10 次请求的限制器,但当超过该限制时,它会返回成功响应,而不是向用户显示速率限制错误。
因此,check_username 可能也应该以类似的方式工作,或者:
-
增加限制;
-
使其可配置;
-
避免将用户名建议/自动验证请求计入同一桶中;
-
或在客户端处理速率限制,而不阻塞注册流程。
我可以在当前的 Discourse 安装环境中复现此问题,包括在 PR #43042 的更改之后,在 https://try.discourse.org/ 上也可以复现。
