收到一条“文档已更新”的回调,JSON 能解析,事件 ID 也没处理过,是否就能让 Agent 读取文件?还缺一个入口判断:这次请求是否通过了约定来源的认证,实际收到的数据是否满足它的签名契约。字段看起来合理、事件编号没有重复,都不能证明发送来源。
本文讨论服务发来的 HTTP 回调,重点是请求跨入业务处理之前的验证。下面用 GitHub 官方协议解释原始请求体的验签要求,再单独以 Stripe 说明投递时效的边界;这些协议不是石墨办公接口说明,也不能拼成一套通用规则。
让接收地址对应明确的协议和验证材料
接入前,先确定这个接收地址对应哪个提供方、哪个端点配置和运行环境。验签材料应从受控配置中选择,而不是让尚未认证的请求随意填写一个租户名,就读取该租户的任意密钥。即使协议提供用于定位验证材料的标识,也应只在预先允许的配置范围内解析,并完成相应验证。
以GitHub 的 Webhook 验签说明为例:配置 Webhook secret 后,接收方使用该 secret 与收到的原始请求体计算 HMAC-SHA256;X-Hub-Signature-256 的值以 sha256= 开头,后接十六进制摘要。应采用适合的恒定时间比较方式,不能仅因签名头存在就放行。官方示例与库需要按真实运行环境接入,本文没有执行这些代码。
这份契约中的受签输入是请求体。不能因为 body 验签通过,就进一步宣称全部 HTTP 请求头都已被签名保护。GitHub 最佳实践介绍的 X-GitHub-Delivery 可用于识别投递,但这个头的用途与 HMAC 实际覆盖的输入应分开确认。
“使用 secret”也不是所有提供方的统一实现。算法、签名编码、输入拼接和验证材料的处理,都要逐项按当前提供方文档核对;未明确的部分应补齐契约。端点从测试切到生产、替换接收地址或轮换验证材料时,应重新确认绑定关系,不用另一家的参数或宽限期补空白。
在 JSON 中间件改写之前保留受签字节
对于明确签原始 body 的协议,验签需要接收端实际收到的字节。网关转发、框架自动解析 JSON、编码转换或日志美化,都可能让后续程序拿到一个已经变过的表示。因此应检查入口链路,把供验签使用的原始输入保留到验证完成;业务解析对象与验签输入不要混成一份。
例如下面两行是为说明问题写的虚构 JSON,不代表任何产品回调字段,也未用于实测:
{"doc":"D1"}
{ "doc": "D1" }
两者解析后可以表达同一个对象,原始字节却不同。若发送方签的是第一行,接收端不能先解析,再按自己的空格、顺序或编码重新序列化成第二行,用它替代收到的 body。反过来,保持旧签名而改写 body,也不能因为业务含义相同就跳过验证。应按该协议对真实输入核验,失败时定位变更发生在哪一层。
原始输入除了 body,还可能包含协议指定的时间戳、版本或其他头字段。保留哪些值、按什么顺序处理,由该来源的签名规则决定;不能声称所有回调都签完整 body。业务字段类型检查仍需要做,但合法的 JSON 结构无法弥补来源验证缺失。
有签名,不等于这次投递仍然新近
Stripe 的 Webhook 文档说明,投递时间戳受签名保护,官方库可按容差检查时效;验证还需要原始请求体、Stripe-Signature 和当前端点的 signing secret。这是 Stripe 的协议要求,不能把它的时间窗口、签名头或密钥处理方式追加到前面的 GitHub 实现中。
接收端应分别确认两件事:用于判断时效的值是否确实在受签范围内,以及当前实现是否实际执行了协议要求的时间检查。仅仅读取一个请求头里的时间,不能证明它可信;仅验证签名数学结果,也不能替代该协议规定的时效判断。时钟偏差、库配置和边界行为应纳入验收,不能为消除错误而关闭时效检查。
新近投递也可能携带旧业务事件。Stripe 会为每次投递生成新的时间戳和签名,重试也如此;所以签名和投递时间不能充当稳定的事件去重键。满足时间规则,不表示窗口内不会重复,更不表示这条通知对应最新文档版本。事件去重和业务顺序仍沿用各自的契约。
失败出口应放在 Agent 可以被触发之前
来源认证失败,或未满足本协议的时效条件时,请求不得进入可触发 Agent 的可信业务任务。不能先启动“读取文档并生成摘要”,再异步补查签名。接收端可以留下受控诊断记录,说明缺签名、编码不符、材料不匹配或时间条件失败,但不把真实 secret、完整签名材料或敏感正文写入普通日志。
通过入口验证后,仍需检查事件类型与动作是否在订阅和处理范围内,再交给既有的事件处理流程。GitHub 的 Webhook 最佳实践也要求关注事件类型和 action。来源可信只说明请求满足这套来源验证条件,不授予任意租户、文档或 AI 数据用途的权限;动作授权失败时,同样不能执行对应读写。
这里不用另一套去重状态机替代入口验证。合法重复的处理,可承接同一条文档回调只处理一次的方法;但首次出现的事件编号不能证明请求可信,已知编号也不能成为跳过验签的通行证。
验收要改变受签输入,而不只测正常回调
可以在隔离测试环境,用合成载荷、测试验证材料和模拟接收器覆盖以下情况。它们是待实施的样本设计,本文未生成载荷、运行 SDK、注册外部回调或发送平台事件。
- 正确协议与错误环境:先建立该提供方的正常样本,再用错误端点或环境的验证材料检查。观察是否被拒绝,以及拒绝之前是否已经生成业务任务。
- 原始字节被改写:在不更新签名的情况下改变空格或编码。对 raw-body 协议检查实际输入验证与失败路径,不能靠重新序列化把变化忽略掉。
- 签名头缺失或格式错误:覆盖缺头、错误版本、畸形编码。失败应停在入口,不能被默认值或“兼容模式”放行。
- 受签时间戳超出范围:仅对有此契约的来源设计过期和时钟偏差样本,核对其真实窗口与库行为;不能给没有对应契约的来源凭空套一个时限。
- 合法重投与越权动作:让同一事件以合法新签名再次到达,确认入口验证与后续去重分别工作;再检查签名正确但目标文档或用途不被允许时,Agent 是否仍被阻止。
记录每个样本实际走过的入口分支、是否进入可信业务任务,以及是否发生文档动作。不能只凭 HTTP 返回码判断后端没有执行。轮换验证材料时,还应按提供方的真实激活、重叠和失效规则复核;没有文档或测试证据,就保留未知,不设置永久旧密钥回退。
把回调协议列进文档集成的验收范围
石墨办公文档中台私有部署公开介绍了 API/SDK 预览、编辑、转换与协作的集成定位。该产品页不能证明某个具体回调的签名头、算法、受签字段、时间窗口或轮换机制;这些仍需向项目所用版本的开发文档和实际接收实现核对。
准备接入时,可带着“接收地址对应什么配置、验签保留哪些原始输入、失败是否能触发 Agent”这三项问题预约演示。先让入口验证有可复核的证据,再将通过的事件交给既有去重与文档授权流程。也可以先看私有化部署方案,了解部署形态与交付范围。