请查看 勘误表 以获取自发布以来报告的所有错误或问题。
另见 翻译。
Copyright © 2018 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and permissive document license rules apply.
WebSub 提供了一种基于 HTTP web 钩子的通用机制,用于任何类型 Web 内容的发布者与其订阅者之间的通信。订阅请求通过 hub 转发,hub 会对请求进行验证与核查。当有新的或更新的内容可用时,hub 会将其分发给订阅者。WebSub 之前被称为 PubSubHubbub。
本节描述了本文档在发布时的状态。其他文档可能会取代本文件。当前 W3C 发布的文档列表及本技术报告的最新修订版可在 W3C 技术报告索引(https://www.w3.org/TR/)中找到。
本文档由 Social Web 工作组发布为一份推荐建议。 欢迎对本文档提出意见。所有相关方都可以通过工作组的 问题追踪器提供实现与漏洞报告及其他意见。这些内容将由 Social Web 社区组讨论,并在本规范的未来版本中予以考虑。
请参阅工作组的 实现报告。
本文档经过 W3C 会员、软件开发者以及其他 W3C 组织与相关方的审阅,并已由 W3C 董事确认为 W3C 推荐规范。 本文档内容稳定,可作为参考材料或被其他文档引用。W3C 在制定推荐规范时的作用,是引起对规范的关注并推动其广泛部署。这提升了 Web 的功能性和互操作性。
本文档由 一个遵循 W3C 专利政策运作的小组生成。 W3C 维护着一个关于本组交付物的专利披露公开列表; 该页面还包含专利披露的说明。任何已知某专利并认为其包含必要权利要求的个人,须按照 《W3C 专利政策》第6节进行披露。
本文件遵循 2017年3月1日 W3C 流程文件管理。
(本节为非规范性内容。)
本协议早期版本称为 PubSubHubbub:
本文件中的“必须”、“禁止”、“必须”、“应当”、“不应当”、“建议”、“不建议”、“推荐”、“可以” 以及 “可选” 的术语,其含义遵循 [RFC2119] 的定义。
WebSub 描述三种角色:发布者、订阅者和 hub。本节针对每种角色的合规标准做说明。
合规的订阅者:
合规的 hub:
本规范退出 CR 阶段时,每项功能至少有两个独立、可互操作的实现。每项功能可由不同产品实现。没有要求所有功能由同一产品实现。为本标准之目的,定义如下术语:
WebSub 发布者是在一个或多个资源 URL 上发布主题和 hub URL 的实现。符合性标准如上面一致性类别所述。
WebSub 订阅者是指给定资源 URL 后,可以发现 hub 和主题 URL、向 hub 订阅并接收 hub 发来的内容分发请求的实现。订阅者 可以 支持认证内容分发。符合性标准如一致性类别所述。
WebSub hub 负责处理订阅请求,并在对应主题 URL 被更新时向订阅者分发内容。hub 必须 支持带有 secret 的订阅请求,并在需要时交付 认证请求。hub 必须 在请求中交付主题 URL 的全部内容,并且 可以 在内容类型支持时,将负载减为增量(diff)。符合性标准如一致性类别所述。
每个实现必须由不同方开发,不能共享、复用或派生自其他合格实现的代码。与本规范实现无关的代码部分可不受此约束。
当 hub 能按定义行为响应订阅者请求、订阅者获得 hub 预期响应且 hub 向订阅者发回规范响应,则该特性在订阅者和 hub 之间视为可互操作。
为评估退出标准,下列每一项都视为一个功能:
发现机制旨在找出至少 2 个 URL。
发布者可以选择向多个 hub 广播和发布,以实现容错和冗余。如果某一个 hub 未能转发文档更新,采用多个独立 hub 能提升通知订阅者的成功率。因此,订阅者可选择订阅多个公布的 hub。
当前协议支持如下发现机制。发布者 必须 至少实现其中一种:
由于 <link> 元素多年限定于 <head> 中,一些代码只会检测 <head>。因此,将 <link> 标签只放在 HTML <head> 中更为稳妥,不宜放入 <body>。
GET /feed HTTP/1.1
Host: example.com
HTTP/1.1 200 Ok
Content-type: text/html
Link: <https://hub.example.com/>; rel="hub"
Link: <http://example.com/feed>; rel="self"
<!doctype html>
<html>
<head>
<link rel="hub" href="https://hub.example.com/">
<link rel="self" href="http://example.com/feed">
</head>
<body>
...
</body>
</html>
执行发现时,订阅者 必须 依次实现以下三种发现机制,并在首次成功后停止:
出于实际目的,rel=self 的 URL 最好只提供单一表现形式。因为 hub 无法获知订阅者请求发现时请求了何种媒体类型([RFC6838])或语言,所以无法用合适的内容版本推送分发。
但可以通过为初始发现请求中的 HTTP 头返回不同的 rel=self URL 来实现内容协商。例如,请求 /feed 时带 Accept 头为 application/json,可返回 rel=self 值为 /feed.json。
下例展示了主题 URL 可根据 Accept 头返回不同的 Link 头:
GET /feed HTTP/1.1
Host: example.com
Accept: application/json
HTTP/1.1 200 Ok
Content-type: application/json
Link: </feed.json>; rel="self"
Link: <https://hub.example.com/>; rel="hub"
{
"items": [...]
}
GET /feed HTTP/1.1
Host: example.com
Accept: text/html
HTTP/1.1 200 Ok
Content-type: text/html
Link: </feed.html>; rel="self"
Link: <https://hub.example.com/>; rel="hub"
<html>
...
同理,也可根据 Accept-Language 头返回不同的 rel=self URL。
GET /feed HTTP/1.1
Host: example.com
Accept-Language: de-DE
HTTP/1.1 200 Ok
Content-type: text/html
Link: </feed-de.json>; rel="self"
Link: <https://hub.example.com/>; rel="hub"
{
"items": [...]
}
订阅主题 URL 包含四个部分,这些部分可以立刻依次发生,也可以有延迟。
取消订阅的步骤相同,只是用单独的参数表示取消操作。此外,Hub 不会向发布者验证取消订阅请求。
订阅由订阅者向 hub URL 发起 HTTPS 或 HTTP POST [RFC7231] 请求开始。此请求必须使用 application/x-www-form-urlencoded 的 Content-Type 头(详见 4.10.22.6 [HTML5]),必须使用 UTF-8 [Encoding] 作为文档字符编码,并必须在请求体中按如下格式包含以下参数:
订阅者 可以根据 hub 要求,添加额外的 HTTP [RFC7230] 参数或请求头。
hub 必须 忽略它不理解的额外参数。
hub 必须允许订阅者重新请求已激活的订阅。每次向 hub 发送的订阅或取消请求必须在对应主题 URL 与回调 URL 的组合下覆盖之前的订阅状态,但前提是操作已被验证(4.3节)。如果验证失败,订阅状态必须保持不变。这样订阅者可以在租期到期前无缝续订。订阅者可以在下次订阅时使用新 hub.secret 或不带 secret。
主题和回调 URL 可以使用 HTTP [RFC7230] 或 HTTPS [RFC2818] 协议。主题 URL 必须是发布者在自引用 Link 头中公布的 URL(见第3节)。Hub 可以 拒绝与发布者声明不符的主题 URL。除此之外,主题 URL 可按 URL 规范自由选择 [URL]。Hub 必须始终对这些 URL 参数解码非保留字符,见 [URL] 1.2节 “百分号编码字节”。
回调 URL 建议为不可猜测的唯一 URL ([capability-urls]),并建议使用 HTTPS [RFC7230]。回调 URL 在 hub 验证订阅和分发内容时起到身份认证作用。同时,回调 建议唯一(不要多 hub 重复使用)且续订时更换。
回调 URL 可以带任意查询参数(如 ?foo=bar&red=fish)。hub 必须在验证时保留查询串,添加新参数时用 & 连接,且不会覆盖已存在的重名参数。Hub 发送内容分发请求时,会将查询参数包含在 URL 部分(而非 POST body)。
若 hub URL 支持 WebSub 并能处理订阅或取消请求,则必须以 HTTP [RFC7231] 202 "Accepted" 响应,表示请求已收到,将进行验证(4.3 节)和确认(4.2 节)。hub 应尽快完成验证。
若 hub 检测到订阅请求有误,必须返回合适的 HTTP [RFC7231] 错误码(4xx 或 5xx)。错误响应应当返回纯文本错误描述,便于客户端开发者定位原因(不用于终端用户)。hub 可以根据自身策略(如域名授权、主题 URL 端口等)拒绝某些回调或主题 URL。但因验证和确认都是异步过程,HTTP 响应禁止依赖验证或确认的进程或结果。
若 hub URL 无法处理订阅或取消请求,可以重定向至另一个支持 WebSub 的 hub,可用 HTTP [RFC7231] 307(临时重定向)或 308(永久重定向)实现。必须包含至少一个 HTTP [RFC7230] Location 头,给出订阅者可用的 hub 新 URL。订阅者需在新 hub URL 重试订阅或取消请求。
hub 可以要求进一步信息以决定是否接受订阅,也可以与发布者核实是否允许订阅。
当订阅被接受时,hub 必须对订阅者进行意图验证。
如果订阅被拒绝,hub 必须向订阅者回调 URL 发送 HTTP [RFC7231](或 HTTPS [RFC2818])GET 请求,通知订阅被拒绝。该请求会追加如下查询参数(格式见 [URL] 第4节):
hub 可在任意时间点(即使之前已接受)拒绝订阅。此时订阅者应当认为无法订阅此主题。
为防止攻击者替订阅者创建/删除订阅,hub 必须确保订阅请求确实由订阅者本人发送。
hub 会向订阅者回调 URL 发送 HTTP [RFC7231](或 HTTPS [RFC2818])GET 请求,附带以下查询参数(格式见 [URL] 第4节):
订阅者必须确认 hub.topic 对应其准备执行的待定订阅或取消操作。如是,则必须以 HTTP 成功(2xx)码响应,且响应体等于 hub.challenge。如不同意该操作,应必须以 404 "Not Found" 响应。
hub 必须将服务器端其他响应码(3xx、4xx、5xx)视为验证失败。如订阅者返回 2xx 但响应体与 hub.challenge 不符,也视为失败。
hub 可以将 hub.lease_seconds 设置为订阅者请求的值,也允许根据自身政策修改。要维持订阅,订阅者必须在租期之前向 hub 重新请求订阅。
hub 必须强制租期到期,不得颁发永久租期。
本规范用 GET 和 POST 区分订阅请求的确认/拒绝和内容分发。虽然这不是 Web 架构层面“最佳实践”,但实现回调 URL 时更简单。由于内容分发请求的 POST body 可能为任意内容类型,仅包含文档内容,故用 GET/POST 区分模式实现上更简便。
当主题有更新时,发布者必须通知此前指定的 hub。hub 与发布者可用任意协议达成一致,只要 hub 最终能将更新内容发送给订阅者。
发布者通知 hub 的具体机制未明确规定。例如,一些公开 hub [1] [2] [3] 要求发布者使用 hub.mode="publish" 和 hub.url=(资源的 URL) 发送 POST 请求。
如果发布者希望将现有订阅迁移到新的主题 URL,可通过 HTTP 重定向实现。
rel=self URL 和 hub,并在新 hub 订阅新主题。这不需要原 hub 参与,无论发布者是否切换 hub 均可用。
当有新内容可用时,Hub 会向订阅者发送内容分发请求。该请求为 Hub 向订阅者回调 URL 的 HTTP [RFC7231](或 HTTPS [RFC2818])POST 请求。POST 请求体必须包含内容分发通知负载。内容分发请求必须有与主题Content-Type一致的 Header,且必须包含主题 URL 的全部内容,下述例外情况除外。
对于 Atom ([RFC4287]) 和 RSS ([RSS-2.0]) feed,hub 可以把已经分发的
atom:entry 或 rss:item 元素从 feed 中移除。
请求必须包含至少一个指向相关 hub 的 rel=hub Link 头 [RFC5988]。还必须包含一个 rel=self Link 头,指向当前被更新主题的规范 URL。Hub 建议将这些合并为一个单独的 Link 头。这些 URL 均源于发现流程(第3节)。订阅者不得用这些 Link 头识别对应的订阅,因为 Link 是内容主题的元数据,而非特定订阅。例如,分发请求中的主题 URL 可能与原始订阅的主题 URL 不同。
订阅者回调 URL 必须返回 HTTP [RFC7231] 2xx 响应码表示成功。订阅者回调 URL 可以用 410 表示订阅已删除,hub 收到后可以终止订阅。hub 必须将其它响应码视为失败;即订阅者不得用 HTTP 重定向进行订阅迁移。订阅者 建议尽快响应分发请求;其成功码建议仅表示收到消息,无需确认是否已成功处理。订阅者响应体必须被 hub 忽略。hub 建议在次数和时长达到自定义重试上限前持续重试分发。若超过自定阈值,hub 不再发送通知,但仍必须保持订阅生效至租期结束,且主题有新消息时,必须继续尝试分发给以前失败的订阅者。
如果订阅请求中带有 hub.secret,hub 必须用该密钥对有效载荷生成 HMAC 签名,并在内容分发请求头中带上该签名。X-Hub-Signature 格式为 method=signature,其中 method 为算法名,signature 为签名的十六进制字符串。签名必须采用 HMAC 算法 [RFC6151],请求体为数据,hub.secret为密钥。
以下是最初注册的算法名,依据 [FIPS-PUB-180-4] 发布时的算法注册表。
未来可扩展支持订阅者声明可用算法。当前大多数 hub 采用 SHA-1 签名,为兼容旧订阅者,尽管其已知存在安全风险。
收到带 X-Hub-Signature 的内容分发请求时,订阅者建议使用同样密钥按算法重算签名。如签名不符,订阅者必须本地丢弃消息。订阅者可以以 2xx 响应码确认,以便异步处理消息和/或抵御暴力破解尝试。结合 HTTPS [RFC2818] 可让简单订阅者安全接收内容分发而无需自带 HTTPS 服务器。
但须注意该签名仅保证载荷未伪造,头信息是否安全还需订阅者自身按需采用 HTTPS 校验。
以下是安全注意事项摘要。需强调 WebSub 是纯 HTTP 的服务器到服务器协议,强烈建议所有请求使用 HTTPS。
订阅者是否应在页面 <body>(以及 <head>)查找 <link> 元素并无定论。忽略 <body> 也是因为部分网站可能让用户上传带 <link> 的内容(虽然目前工作组未见具体案例)。若用于发现,用户可恶意让所有订阅者用替代 hub 推送恶意内容。因此,仅在 HTML <head> 发现更安全。
首先,订阅者建议优先选择 hub 的 HTTPS URL(即使是 HTTP 提供)。其次,订阅者建议为回调用唯一、不可预测的能力型 URL 且用 HTTPS。最后,订阅时建议附带 hub.secret 便于内容签名。
hub 建议强制较短的 hub.lease_seconds(10 天较合适)。在验证意图时,hub 建议使用随机单次 hub.challenge。
Hub 必须使用订阅者原回调(含 HTTPS)。如请求带 hub.secret,Hub 必须用其签名。
若订阅请求带 hub.secret,订阅者建议校验签名,且必须按 server 声明的机制校验,不通过则丢弃请求。
若订阅者不用安全回调(HTTPS),或 hub 与订阅者间 TLS 可能被破坏,则内容通知的完整性仅依赖 hub.secret 和摘要算法。此时应按应用安全需求选用算法。由于 SHA-1 已不安全,建议至少用 SHA-256。
下列问题基于《安全与隐私自评问卷》([security-privacy-questionnaire]),概述本规范的安全与隐私考虑。
本节为非规范性内容。
编辑感谢 PubSubHubbub 的作者、IndieWeb 社区及各位实现者的支持、鼓励与热情。特别地,编辑感谢 Brad Fitzpatrick、 Brett Slatkin、 Martin Atkins、 Amy Guy、 Barry Frost、 Benjamin Roberts、 Eugen Rochko、 Jordan Potter、 Matthias Pfefferle、 Malcolm Blaney、 Marten de Vries、 Sandro Hawke、 Tantek Çelik, 以及 Tony Garnock-Jones。
本节为非规范性内容。
.host-meta 发现特性(Issue
#97)hub.secret 应加密随机且唯一Accept-Language 头返回不同的 rel=self URL<link> 标签放在 HTML <head> 元素内rel=self URL 支持内容协商的范例hub.topic 必须是发现到的 self URLFrom 头的表述