Sign inGet Started

Verify 最佳实践

Verify 出厂的默认值适用于大多数一次性验证码流程。本指南涵盖默认值未替你做出的决策:验证码应存活多久、需要多少位数字、哪种渠道适合你实际验证的内容,以及如何区分投递问题和验证问题。

凡公开标准有相关规定,本指南会引用原文。验证码身份验证的标准是 NIST SP 800-63B,即《数字身份指南:身份验证与身份验证器管理》,获取于 2026 年 9 月 3 日。引文均出自当前修订版。许多 OTP 建议仍在引用的旧版 Revision 3 已带有 NIST 自己的标注:自 2025 年 8 月 1 日起被 SP 800-63-4 取代。

验证码保持六位或更多

Bird 的验证码默认为 6 位,options.code_length 接受每次请求 4 到 8 位(验证设置)。默认值是正确的选择,而范围的下限则不是:

The verifier SHALL generate random authentication secrets that are at least six decimal digits (or equivalent) in length using an approved random bit generator

4 位或 5 位验证码在 API 接受的范围之内,却在标准允许的范围之外。仅在你已确定验证码熵值不承担安全职责的流程中才缩短验证码。

有效期保持在十分钟或更短

Duration 默认为 10 分钟,接受 1 到 999(验证设置)。默认值恰好处于标准的上限:

In all cases, the authentication SHALL be considered invalid unless completed within 10 minutes.

因此可配置范围远超标准的上限。提高 Duration 以减少用户因验证码过期而联系客服,是唯一一个会悄然使流程偏离 800-63B 的设置更改。如果验证码在用户收到之前就过期了,通常应修复发送路径而非延长有效窗口。

预期两层请求速率限制

校验尝试受到两重限制,两者应对不同的威胁。

你的设置。 Maximum Retries 默认为 5,接受 1 到 10;超过此值验证将以 attempts_exhausted 失败。标准要求对这种长度的验证码必须存在此上限,并附加了一条值得了解的规则:

Generating a new authentication secret SHALL NOT reset the failed authentication count.

该上限有一个天花板,而这是 Verify 提供的每个值都完全符合标准的唯一之处:

the verifier SHALL limit consecutive failed authentication attempts using a specific authenticator on a single subscriber account to no more than 100 by disabling that authenticator

Maximum Retries 最高为 10,因此你能选择的任何配置都不会达到 100。这与上面两节正好相反:验证码长度和有效期各有一个受支持的值会偏离 800-63B,而尝试次数限制没有。

Bird 的平台上限,无论你如何配置都会生效:每个地址每滚动小时 5 次发送(包含创建和重新发送),每个收件人地址集每分钟 10 次校验。两者均返回 429 并附带 Retry-After 请求头(滥用防护)。这些限制的目的是防止 OTP 流量被利用来消耗你的费用或骚扰他人的手机,因此应将此处的 429 视为系统正常工作,而非需要绕过的限制。

根据验证目的选择渠道

Verify 通过电子邮件、SMS、WhatsApp 和 Telegram 投递(渠道)。标准对其中两种的处理方式截然不同,区别在于验证码的 用途 而非承载它的渠道。

电子邮件不是身份验证因素。 800-63B 明确指出:

Email SHALL NOT be used for out-of-band authentication because it may be vulnerable to:

  • Access using only a password
  • Interception in transit or at intermediate mail servers
  • Rerouting attacks, such as those caused by Domain Name System (DNS) spoofing

同一段落为 Verify 电子邮件渠道所针对的场景做了例外规定:

Confirmation codes that are sent to validate email addresses or are issued as recovery codes (see Sec. 4.2.1.2) are not authentication processes and not affected by the above prohibition.

因此,用于证明某人控制某地址的电子邮件验证码属于标准例外的范围,是合规的。用作登录时第二因素的电子邮件验证码则不合规。如果你的流程属于后者,请勿使用电子邮件渠道。

SMS 和语音被允许但有限制。 通过电话网络投递附带三项义务,出现在连续两句话中:

Accordingly, verifiers SHALL ensure that alternative authenticator types are available to all subscribers and SHOULD remind subscribers of this limitation of PSTN out-of-band authenticators before binding one or more devices controlled by the subscriber. Verifiers SHOULD consider risk indicators (e.g., device swap, SIM change, number porting, other abnormal behavior) before using the PSTN to deliver an out-of-band authentication secret.

SHALL 是产品层面的要求而非 Verify 的设置:如果 SMS 是你唯一的因素,你就不满足该要求。Verify 的按国家渠道顺序有助于投递,因为在大多数国家 WhatsApp 会优先尝试,SMS 作为备选(渠道),但 SMS、WhatsApp 和 Telegram 都是同一身份验证器类型(带外)的投递路径,因此在它们之间切换并非提供替代类型。两个 SHOULD 是产品团队需要实现的:在用户绑定手机号时给出提示,以及在发送前检查号码携转和 SIM 卡变更信号。

先分析失败原因再重新发送

未送达的验证码和送达后被输错的验证码需要不同的处理方式,事件可以区分两者(事件)。

verify.attempt.undelivered 携带一个 reason,后续行为取决于收件人的渠道方案。对于有多个可用渠道的收件人,这不会结束验证:Bird 会推进到下一个渠道,该渠道获得自己的 verify.attempt.sent,因此单次未投递的尝试不意味着验证终止,也不需要你重新发送。对于单渠道收件人,没有可推进的渠道,验证随即结束。

该结束状态需要单独处理,有一个事件可以识别它。failed 状态是共用的:当提交了过多错误验证码时(reason attempts_exhausted)和当没有任何计划渠道能投递验证码时(reason undeliverable),验证都会进入该状态。只有第二种情况会发出 verify.verification.failed。因此对于单渠道收件人,告诉你没人收到验证码的是该事件而非状态。

有一个需要在设计阶段就考虑而非事后才发现的特性,而且这是 Bird 自身的特性而非标准的要求:事件说明投递报告是参考性的而非保证性的,因为运营商和邮箱提供商在确认内容和响应速度上各不相同。在某些市场,尝试事件可能延迟数分钟到达,或者无法区分投递和受理。请基于 POST /v1/verify/verifications/next-channel(它会推进验证)构建 "I didn't get a code" 路径,而不是等待可能不会到来的投递事件。

重试请求而非重试验证

Verify 的三个端点均接受 Idempotency-Key(安全重试)。在超时或连接中断后,使用相同的 key 重试会重放原始响应:不会发出第二个验证码,也不会消耗校验尝试次数。没有 key 的情况下,丢失响应后的重试会发送另一个验证码,并从每小时发送配额中再扣一次。

了解每次发送的费用

计费按每个已发送的验证码计算,费率取决于渠道和目的地,重新发送或备选渠道的每次发送都会产生额外费用(费用与计费)。两个影响重试策略的结论:

  • 经过多个渠道回退的验证会产生多次发送费用,因此宽泛的渠道方案既是投递决策也是成本决策。
  • Telegram 的计费时点不同。Telegram 会先查询该号码是否能接收消息,当答案为是时按全球统一费率计费。无法触达的号码不收费,并推进到下一个渠道且不产生账单。因此收费意味着已被接受投递而非已送达:如果验证码之后未能投递,费用仍然存在,且验证在回退到的渠道上再次付费。如果不希望产生第二次费用,请在 Countries 页面将 Telegram 从相关国家的渠道顺序中移除(费用与计费)。

后续步骤