当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > Label Studio webhook 任务完成回调的签名处理

Label Studio webhook 任务完成回调的签名处理

来源:17golang原创 2026-10-28 10:12:51 0浏览 收藏

Label Studio webhook 的签名处理要先区分原生能力与自建协议:普通 webhook 可配置认证请求头,但不能把静态头值当作逐次计算的正文签名。若接收系统要求 HMAC,可在确认来源身份的自建中继上计算签名,再由业务接收端校验。本文中的签名头均为自定义协议,不是 Label Studio 内置字段。

官方文档地址:https://labelstud.io/guide/webhooks.html

为什么回调到了,接收端仍在报缺少签名

以一个标注结果接入服务为例:接收代码要求每次请求携带动态摘要,而 Label Studio 仅配置了回调地址。此时先核对双方协议,不能直接假定存在 X-Hub-Signature 一类头。官方普通 webhook 配置允许添加自定义 header;公开发送实现也展示了配置 headers 与 JSON 正文的发送方式,没有本文中时间戳加正文的 HMAC 生成逻辑。这一判断针对普通 webhook,不代替定制部署或其他版本的接口约定。

静态认证头适合让入口识别持有共享凭据的发送方,应通过 HTTPS 传输并限制访问;它不随正文变化,也不独自阻止已捕获请求重放。不要在配置页预填一个固定的“签名”字符串,然后期待正文变化时它自动更新。

先把“任务完成”对应到真正的业务事件

常见接入点是 ANNOTATION_CREATED。官方事件参考将它定义为任务新增标注时发送的事件,载荷含 annotation.id、annotation.task、annotation.was_cancelled 等信息。业务上需要多少份标注、是否审阅通过、是否允许跳过,应由项目规则判断;不能把收到一次创建事件直接解释成任务全部完成。

因此入口需保留 action、项目标识与标注标识,再由后台判断是否满足业务完成条件。was_cancelled 为 true 的跳过标注不应未经判断进入“有效完成”分支。若仅发送 action 而关闭完整 payload,接收端也就没有足够字段执行这些判断。

需要动态签名时,明确中继的信任边界

可采用两个独立边界:Label Studio 到中继使用 HTTPS 与专用静态认证头;中继认证成功后,以保留的原始正文生成 HMAC,再发送给业务接收端。中继必须拒绝未认证来源,覆盖而不是透传外部传入的签名头,并禁止绕过它直达业务端。中继签名证明的是中继认可并发送了这些字节;若源凭据泄露,签名无法恢复源身份的可信性。

Label Studio来源认证和自建中继签名的静态关系图
信任边界结构图:来源认证和中继签名是两种机制,连线表达依赖关系,并非运行记录。

下面定义一个原创的专用路由协议:只接受 POST /hooks/label-studio,不接收查询参数;签名消息包含协议版本、固定路由、十进制秒级时间戳、随机 nonce 和原始正文。中继使用独立随机密钥,双方从受控配置加载相同的密钥字节,不复用 Label Studio API token。

自定义请求头含义
X-Relay-Time发送时间,秒级十进制字符串
X-Relay-Nonce每次投递新建的32位小写十六进制随机值
X-Relay-Signaturesha256= 前缀加64位小写十六进制摘要

这些名字只属于本例。中继可用 secrets.token_hex(16) 创建 nonce;接收端从真实请求读取头和原始 bytes。JSON 解析后重新序列化可能改变空白或编码,必须先针对实际传输的 bytes 验签,再解析业务内容。

把签名计算与防重放存储分开实现

以下函数只演示消息格式、时间窗与摘要比较,没有启动 HTTP 服务。调用方必须在读取正文时限制大小、拒绝重复协议头,并确认真实方法、路径和无查询参数;不能对其他路由复用这段固定路由消息。

import hashlib
import hmac
import re

def message(raw_body: bytes, timestamp: str, nonce: str) -> bytes:
    # 固定协议版本和接收路由,末尾正文保持原始字节。
    prefix = f"v1\nPOST\n/hooks/label-studio\n{timestamp}\n{nonce}\n"
    return prefix.encode("ascii") + raw_body

def make_signature(key: bytes, raw_body: bytes,
                   timestamp: str, nonce: str) -> str:
    # key来自受控配置;发送端转发的正文必须就是raw_body。
    digest = hmac.new(
        key, message(raw_body, timestamp, nonce), hashlib.sha256
    ).hexdigest()
    return "sha256=" + digest

def valid_signature(key: bytes, raw_body: bytes, timestamp: str,
                    nonce: str, supplied: str, now: int) -> bool:
    # 先限制格式,避免非ASCII头值、巨大整数或错误摘要格式。
    if not re.fullmatch(r"[0-9]{1,12}", timestamp):
        return False
    if not re.fullmatch(r"[0-9a-f]{32}", nonce):
        return False
    if not re.fullmatch(r"sha256=[0-9a-f]{64}", supplied):
        return False
    if abs(now - int(timestamp)) > 300:
        return False
    expected = make_signature(key, raw_body, timestamp, nonce)
    # 使用恒时比较函数;通过此处还不代表完成防重放。
    return hmac.compare_digest(expected, supplied)

300 秒是本例自定的允许时钟偏差,不是产品规定。接收端使用自己读取的当前时间,维护时钟同步。通过摘要校验后,还要在共享持久化存储中原子登记 nonce,禁止多进程分别用普通内存集合做去重。记录至少保留到该时间戳不可能再次被时间窗接受:允许未来300秒时,固定从接收时保留300秒不一定够。

推荐将已验签事件的持久化收件记录与 nonce 唯一性放在同一事务中;提交成功才返回成功响应,异步工作者按业务幂等键处理。若先消耗 nonce,随后落库失败,又把后续重投当成成功重复,可能丢事件。重复投递的响应应依据已持久化状态决定,不能只看 nonce 存在就宣称业务完成。

验签输入与持久化收件职责的静态关系图
接收职责结构图:摘要输入、时间窗口、nonce记录与持久化事件各自约束不同问题。

怎样判断修复方向正确

下面是供接入时采用的预期用例,不是本文已经执行的测试结果。它们针对中继与业务服务自定义协议,不需要把产品改造成原生支持这些头。

输入变化预期判断
正文改一个字节,沿用原摘要摘要不匹配,拒绝
时间戳超过约定窗口拒绝,不进入业务处理
同nonce与同事件再次到达查询持久化收件状态,不重复产生副作用
验签通过但action不在允许集合不执行标注完成处理
标注创建但未满足项目完成规则保存事件并重新判断任务状态,不提前完成任务

接入时容易混淆的三个问题

只在接收端增加 HMAC 代码够吗?不够。发送端必须按同一协议生成动态签名;普通静态 header 配置不会替你计算它。无需动态签名的内网接入,也可按实际需求采用 HTTPS 与来源认证,无须虚构额外机制。

签名成功等于任务已完成吗?不等于。签名回答字节完整性与持钥身份问题;action、项目归属、有效标注和完成规则仍需业务判断。

记录哪些排查信息?记录事件类型、项目和标注标识、接收状态及错误类别;不要把认证头、密钥或含个人信息的完整标注正文写入普通日志。保留可补偿的收件状态,比只记录HTTP状态更有用。

资料:官方事件字段说明;普通 webhook 发送实现;Python HMAC 与摘要比较。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
GitHub Actions 可复用工作流的输入边界变化GitHub Actions 可复用工作流的输入边界变化
上一篇
GitHub Actions 可复用工作流的输入边界变化
GitHub Copilot 代码审查证据链的工作流调整
下一篇
GitHub Copilot 代码审查证据链的工作流调整
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    754次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    752次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    833次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    798次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    633次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码