返回知识中心
平台

跨源隔离回滚:改动响应头的检查清单

先确定哪些 URL 必须保持隔离、每类嵌入资源归哪个策略方负责,再用分阶段上线,并在某个用户旅程中断时退回旧的响应头组合。

BotBrowser Team

跨源隔离回滚:改动响应头的检查清单
文档中心

想直接进入 平台 文档吗?

这篇文章属于博客内容库。若你要步骤化配置、参考说明和持续更新,请直接进入对应 docs 分区。

跨源隔离的改动是对线上产品的一次响应策略变更,难的不是把它打开。这份清单从修复 not cross-origin isolated之后开始:第一个页面已经证明能进入隔离状态之后,还要决定哪些 URL 必须保持隔离、每类嵌入资源由谁负责,以及分阶段上线在某个用户旅程中断时该怎么收。

BotBrowser 支持在受控上下文中加载声明的浏览器版本并记录该响应的 window.crossOriginIsolated,可用于测试隔离范围;它不能替某个主机发送 COOP 或 COEP,不能改变第三方资源的 CORS 或 CORP 应答,也不保证用户旅程在新策略下仍然可用。

跨源隔离回滚就绪清单

TL;DR

  • 列出需要隔离的 URL,其余路径要有意保持不隔离。
  • 改响应头之前,把每一类嵌入资源对应到一个策略负责人。
  • 按比例或按主机分阶段上线,不要一次打开所有路由。
  • 第一个生产请求之前就约定回滚触发条件和旧的响应头组合。

目录

划定需要隔离的 URL

隔离属于文档,不属于整个站点。产品通常只需要在一个面上隔离,比如一个启动带共享内存 Worker 的页面,而产品介绍页、登录流程或 OAuth 回调都不需要。向所有路径发送 Cross-Origin-Opener-Policy: same-origin 和 Cross-Origin-Embedder-Policy: require-corp 只会扩大影响面,对那些从不使用隔离专属特性的页面没有任何好处。在扩大范围前,先重读CORS 边界指南,那里说明了哪些资源已经算作跨源依赖。

把范围写成 URL 模式列表,例如 /app/editor/* 和 /auth/*,而不是一个域名。里面要列出必须隔离的文档路径、必须保持不隔离的路径,以及弹窗行为改不动的回调路径。以隔离文档结尾的重定向链仍然是隔离的,以不隔离文档结尾的则不是;把范围写成「整个应用」时,这个差别最容易被漏掉。

把范围和上线记录放在一起(docs/cross-origin-isolation-scope.md),写上日期和负责人。以后新增路由时,范围会告诉下一位工程师它继承父模式的隔离状态,还是需要自己的策略。这正是「有意的策略」和「不小心蔓延开的策略」的区别。

核对范围要对着站点地图和路由清单,而不是凭记忆;如果路由经常变化,就把这个比对做成定时任务。由模板生成的路由可能提供一份响应头规则没覆盖到的文档,尤其是当模板有自己的布局和不同的规范主机时。隔离最容易在团队没想到的地方让人意外。

让范围可以被复核

范围复核也是确认不隔离页面真的不需要隔离专属特性的时刻。在把某个页面划出范围之前,先在应用里搜 SharedArrayBuffer、crossOriginIsolated 和 Atomics。检查布尔值并回退的页面可以安全地不隔离;不检查就直接构造共享缓冲区的页面,一旦走到那段功能就会抛错。

如果某个页面只在特性开关开启时才需要隔离,把开关和模式一起记下来。在不发送该策略的主机上打开开关,会产生一个看起来像代码缺陷的运行时失败,而开关记录能让复核者看出策略从未生效,而不是功能出现了回归。

把范围写成可机器检查的清单

放在演示文稿里的范围没法检查,所以预期状态应当写成 scopes.tsv 的一行,或构建可以读取的 JSON 字段。把它写成路径模式加每个模式的预期状态,这样后来的改动可以和它比对,而不是争论。一条有用的记录会写明模式、预期隔离状态、原因,以及提出这个要求的负责人。

预期状态并不总是「隔离」。多数产品需要某个文档保持不隔离,是因为嵌入的客服组件、结算 iframe 或旧报表页依赖了同一窗口期内没人能改的资源。把这种意图记下来,才能避免后来的清理把有意排除的页面「修好」,并在过程中打断一个用户旅程。

按响应路径而不是只按 URL 前缀给模式分组。重写之后,同一个前缀下的两条路径可能由不同来源提供,作用于其中一条的 CDN 规则未必作用于另一条。条目超过一小批之后,再加一条检查:每个带版本的路由要么被某个模式匹配,要么被显式排除,这样新路由不会悄悄继承错误的状态。

把清单放在部署配置旁边,并在新增或删除路由的同一次改动里更新它。事后再更新的范围,在生产里已经错过了,而它造成的失败看起来像随机的不一致,而不是少了一条记录。

在同一构建上做前后对比

让上线可复核的是一次比较,而不是一次读数;把它记成 scope-before.json 和 scope-after.json。改动之前先在预发主机上取基线,然后在同一浏览器版本和同一批夹具上重复同样的检查。只有改动后的一次读数,无法区分一个意外值来自策略还是本来就存在。

每个页面记三个字段:实际送达的响应头取值、运行时的隔离布尔值,以及如果存在的话第一个失败的资源。响应头说明服务器发了什么,布尔值说明浏览器接受了什么,失败资源说明前两者不一致时该找哪一类资源。

每个模式至少抽一个 URL,而不是整体只抽一个。重定向链、本地化主机和尾斜杠变体都可能产生不同的最终响应,而模式的强度取决于它最没被测过的那个成员。

CDN 变更、service worker 更新或托管迁移之后,即使应用代码没动,也要重复测量。这三件事会在不碰仓库的情况下改变送达的响应,这也正是变更之前的策略记录可能误导人的原因。

把资源类别对应到负责人

隔离会改变什么能加载,所以每一类嵌入资源都需要一个决定和一个负责人。按资源如何被提供来分组,而不是按哪个团队提出需求来分组,因为策略答案取决于响应,不取决于请求。

Frame 是最敏感的一类。跨源 iframe 需要一份与 COEP 兼容的响应或一个 CORP 响应头,而供应商的 frame 不发这些,一旦嵌入文档强制执行 COEP 就会失败。基于弹窗的登录属于同一类:COOP 分隔 opener 关系,依赖 window.opener 的供应商会立刻察觉。记录供应商、frame 来源,以及供应商文档对隔离兼容性怎么说。

脚本、样式表、字体和图片是下一类。它们通常由 CDN 提供,答案是 CORS 响应头或 Cross-Origin-Resource-Policy: cross-origin。对某个子域生效的 CDN 规则未必作用于规范主机,所以要写的是能为确切被你调用的那个主机改响应的人。

媒体自成一类,通常改起来最慢。视频和音频端点可能是签名的、重定向的或分区域的,每一种都会改变你看到的响应头。用接近生产的签名 URL 去测,而不是用静态样例。

分析脚本和标签管理器是最可能静默失效的一类。它们中很多会在运行时再加载其他脚本,你复核的 URL 不是实际运行的 URL。记录它们可能失败,并决定这种失败是否可接受,因为一个停止上报的分析信标很少阻断用户旅程,但确实会改变你依赖的数据。

对每一类,写出策略、负责人和验证方式。验证是每类一次请求,记录最终 URL 和让它兼容的那个响应头取值。没有验证的类别只是你在假定,而不是知道;而假定正是把分阶段上线变成意外的东西。

分阶段上线

按流量分阶段,而不是按雄心分阶段。分阶段上线让第一批生产证据来自一个小群体,同时旧策略仍然只差一次配置改动。选择哪一维度分阶段,要看你的基础设施能最快还原什么:边缘的百分比、单个主机上的响应头规则,或者一条跑同样应用代码的金丝雀路由。

让分阶段单位与用户旅程对齐。按流量百分比分流,可能把同一个用户在第一个页面送到隔离版本,在回调时送到不隔离版本,产生的失败看起来随机,实则不是。粘性百分比或按主机分组,能让一次旅程在期间内始终落在同一种策略上。

决定各个面的顺序。先做嵌入依赖最少、回退最清楚的那个面,最后做承载登录或支付的那个面。这个顺序的意义在于,先在便宜的地方了解这些资源类别,再去动昂贵的地方。

在窗口期观察正确的信号。本该为 true 的页面上出现 crossOriginIsolated: false,是配置结果。某个资源被阻止,是针对某一类的策略结果。用户无法完成登录,是应用结果,它的优先级高于前两者,因为再多的隔离状态也不值得一个断掉的旅程。

窗口期要短到可以还原,又要长到能看到完整旅程,并把它写进上线记录,免得有人不小心延长。在用户来得及登录再通过回调返回之前就结束的窗口,并没有测到最可能出问题的那些路径。

定义回滚

回滚需要触发条件、动作和验证,三者在开始上线前就约定好。触发条件是一句支持工程师不用问发布负责人就能执行的话:某个关键旅程在已分阶段的用户里失败比例超过某个值,或者某个类别的负责人在窗口期内没能让资源兼容。

动作是旧的响应头组合加上旧的应用路径,两者都要保持可用并测过。只恢复 COOP 和 COEP 而保留隔离专属功能,会得到一个报告 false 然后在运行时失败的页面;应用路径必须随策略一起改。

验证是证明隔离状态的那套检查反过来跑一遍:分阶段主机回到旧响应头,crossOriginIsolated 在预期处为 false,回退路径走完旅程,需要新策略的资源类别不再被要求。把结果连同日期和做出决定的人记下来。

写下回滚不覆盖什么。缓存文档、service worker,或带很长 TTL 的边缘规则,可能在源站改了之后继续送出新策略。记下清除步骤和涉及的主机名,因为不完整的回滚是一种看起来已经还原、实际没有的策略。

上线结束后复核回滚记录,不管用没用上。没用上的回滚同样能告诉你动作是否有效,下一次改响应头可以直接复用这套流程,而不必重新摸索。

带上回退路径与证据

选择产品真的能上线的回退路径

每个隔离专属功能都需要一条用户能走完任务的回退路径(回退路径值得单独一个测试用例,如 fallback: passed),而且回退应当是更简单的那条,而不是第二套系统。带共享内存的 Worker 可以回退到主线程循环加进度提示。为了性能才需要隔离的功能,可以退化到较慢的路径并给出可见提示。为了正确性才需要隔离的功能没法回退,那是应当在上线前而不是上线中上报的产品决定。

用测隔离路径的同一批夹具去测回退路径。从没在接近生产的数据下跑过的回退就是没测过的路径,而上线窗口不是发现它在较大输入上抛错的时候。

决定谁拥有回退路径。如果是应用团队交付、平台团队打开响应头,那么回退归应用团队,应当进入发布清单而不是事后补充。上线记录里要点名回退和它的验证位置,这样支持工程师可以直接把用户指过去,而不必等诊断。

证据要短到能马上行动

只有专家看得懂的运行记录,在故障期间不会被读。把它压到范围清单、类别负责人、分阶段维度、改动前后的读数、回滚触发条件和回退路径。每一项应当占一行,必要时指向更长的文档。

给记录写上日期,并写明接受该策略的人,以及计划窗口期所用的本地时区。没有负责人的记录会变成没人维护的记录,下一次改动会从比第一次更弱的位置开始。

当策略稳定到旧的响应头组合在任何可部署配置里都不再存在时,再把这记录归档。在那之前,这份记录是当前状态如何形成的唯一描述。

实践结论

隔离是一项带范围、负责人和上线计划的响应策略。定下 URL,理清资源类别,按流量分阶段,并在第一个生产请求之前约定回滚触发条件。这份清单要和跨源隔离修复、CORS 边界指南以及安全上下文概览一起使用。

来源

#跨源隔离#COOP#COEP#上线#回滚

本文属于指南: Web 平台能力面:一致性复核地图

让 BotBrowser 从研究走向生产

先用这些指南理解模型,再进入跨平台验证、隔离上下文和面向规模化的浏览器部署。