Web Bot Auth 验证
此技能为现有服务添加已签名智能体流量的验证能力。难点不在于密码学本身,而在于那些让签名看起来比实际更有保障的细微细节:未签名的组件、重复使用的 nonce、会触发请求的头、重建方式与客户端签名不一致的 URL。此处的参考代码和测试已将这些细节编码实现。你的工作是将它们正确集成到用户的技术栈中,而不削弱其安全性。
规范状态:目标为 draft-meunier-web-bot-auth-architecture-05(一个 IETF 互联网草案,非标准),同时兼容许多已部署智能体仍在发送的旧式 quoted-string Signature-Agent。告知用户这是草案,可能会发生变化。
工作流
-
找到请求入口点。 确定框架以及请求进入的位置(中间件、边缘处理器、反向代理钩子)。注意服务是否位于代理或 CDN 之后,因为这决定了如何重建 URL(参见
references/adapters.md)。 -
与用户确定策略决策。 阅读
references/policy-decisions.md,并就对话中尚未确定的事项进行询问:允许哪些操作符、是否要求请求绑定和 nonce、nonce 存储方式,以及每种结果应执行什么操作。不要默不作声地选择严格设置:严格的默认值会拒绝现今大多数已部署的智能体。也不要默不作声地选择宽松设置:要说明它们留下了哪些安全漏洞。 -
添加验证器。
- Python:将
scripts/web_bot_auth.py原样复制到项目中(保留其许可证头),将cryptography添加到依赖项,并编写一个小的适配器来构建Request。 - 其他语言:移植
scripts/web_bot_auth.py,使用该语言中成熟的 Ed25519 库(绝不使用自行实现的库),如有现成的 RFC 8941 结构化字段解析器则使用之。移植完成的标准是能通过assets/test-vectors.json中的每一个测试用例。
- Python:将
-
谨慎编写适配器。 这是大多数集成出错的地方。
Request必须携带实际发送的方法、客户端请求的绝对 URL、每个头部出现的所有值以及解析或解压缩之前的原始请求体字节。各框架的注意事项请参见references/adapters.md。 -
选择合适的 nonce 存储。 单个进程仅使用内存存储。超过一个 worker 或节点的情况需要共享的、强一致性的存储。参见
references/nonce-stores.md。 -
将结果映射为具体动作。 除非用户另有决定,否则使用下表。
-
端到端测试集成。 通过真实框架(使用其测试客户端)编写测试,使用
sign_request生成已签名请求:包括一个有效请求、一个被篡改请求体的请求、一个重放请求和一个已过期请求。如果复制了scripts/test_web_bot_auth.py文件,也要运行它。 -
建议先以仅日志模式推出。 在强制执行任何操作之前,先在一段时间内记录每个请求的
outcome、reason、operator、keyid、binds_request和replay_protected。
结果
| 结果 | 含义 | 默认操作 |
|---|---|---|
unsigned | 没有 web-bot-auth 签名 | 现有的机器人处理 / 风险评分 |
verified | 有效签名,已列入白名单的操作符 | 允许为对应智能体;仍需应用用户级认证和速率限制 |
signed_untrusted | 有效签名,已知但未列入白名单的操作符 | 视为未签名,可选地降低风险等级 |
unknown_operator | 格式正确,但无可用密钥 | 视为未签名;可选地触发后台目录获取 |
invalid | 签名存在但错误 | 拦截。合法客户端不会发送错误的签名 |
expired | 超出时间窗口 | 拦截 |
replay | nonce 已被使用 | 拦截 |
对于后三种结果,Result.hostile 为真。对于 verified 结果,还需检查两个标志。如果 binds_request 为假,则签名未覆盖方法、路径、查询参数和请求体。如果 replay_protected 为假,则表示没有 nonce 或存储不可用。一个合理的默认行为是:对于安全方法(GET,HEAD),即使其中任一标志为假,也允许通过已验证的请求。
容易被忽略的规则
这些规则之所以存在,是因为每一条背后都有具体的攻击场景。如果用户的约束似乎需要打破某条规则,请解释风险并让其自行决定。
- 绝不要为尚未列入白名单的操作符内联获取密钥目录。
Signature-Agent是攻击者可控制的值,因此按它指定的内容去获取就是 SSRF,并且会让任何人拖慢你的请求路径。应在后台任务中使用safe_fetch_directory获取未知操作符,或者干脆不获取。 - 当
Content-Digest被签名覆盖时,务必将其与请求体进行验证。 对头部签名但没人检查它是否与收到的字节匹配,这毫无意义。 - 仅在签名验证通过后才记录 nonce。 否则任何人都可以填充存储,或预先消耗掉合法智能体的 nonce。
- 跨实例使用共享的、原子的 nonce 存储。 每个进程独立的缓存会使重放请求命中不同的 worker。
- 用
created而非仅expires来约束新鲜度。 智能体可能使用很长的过期时间签名;无论过期时间如何,Policy.max_age都能保持重放窗口和 nonce 缓存较小。 - 重建客户端实际请求的 URL。 在代理后面时,仅从你信任的代理获取转发的 scheme 和 host。如果重建有误,有效签名将验证失败,或者伪造的 Host 头部会被信任。
- 有效签名只标识操作符,不标识用户。 在其后方应保持正常的认证以及用户级授权和速率限制。绝不要让签名代替用户对敏感操作进行授权。
- 不要对已验证的智能体应用人类行为检测。 它们本质上是自动化程序。
文件
scripts/web_bot_auth.py:验证器、密钥解析器、nonce 存储和sign_request。单个文件;仅依赖cryptography。scripts/test_web_bot_auth.py:攻击案例测试以及 RFC 9421 附录 B.2.6 的一致性向量。从scripts/目录运行python -m unittest -v test_web_bot_auth。scripts/generate_vectors.py:重新生成assets/test-vectors.json。assets/test-vectors.json:针对移植的语言无关测试向量(仅包含测试密钥)。references/policy-decisions.md:需要与用户一起做出的选择及其权衡。在步骤 2 中阅读。references/adapters.md:在常见框架和代理后正确构建Request。在步骤 4 中阅读。references/nonce-stores.md:哪些存储是安全的以及如何调整其大小。在步骤 5 中阅读。