系统设计 / 访问控制 · 从 RBAC 查表到 policy 判定函数 / HTTP 中的 policy:浏览器是一台 PDP 待审核 19 / 23
实例 · HTTP / 浏览器

HTTP 中的 policy:浏览器是一台 PDP

打开浏览器的 Network 面板,会看到许多带 Policy 字样的响应头:Content-Security-PolicyPermissions-PolicyCross-Origin-*-Policy……它们并非杂乱无章的开关,而是同一件事的不同侧面:服务器声明策略(PAP),浏览器当场判 permit / deny(PDP)并执行(PEP)。而且它们大多能精确解码回 授权请求 的那个四元组——只不过其中的 principal(who)是 originscheme + host + port

browser.decide(origin, load / fetch / use, resource / feature) → allow | block

注 · 地基是同源策略(Same-Origin Policy),它是整个 web 的 default deny——默认情况下,A 源的脚本读不到 B 源的响应、碰不到 B 源的 DOM。那些 *-Policy 头,要么是放宽这条默认拒绝(CORS),要么是在它之上再收紧(CSP / Permissions-Policy)。它们和 组合算法 一脉相承:先定默认决策(deny),再用显式规则增删。

1 · CORS:服务器声明「谁能跨域读我」

跨域请求里,who 是请求方 Origin、action 是 HTTP method、what 是目标资源。服务器用一组 Access-Control-Allow-* 响应头声明它的放行策略,判定却发生在浏览器端。

图 1-1 · 固定服务器 https://api.example,请求与服务器策略变化时浏览器的 CORS 判定。可调 Origin、method、凭据与三个 Allow-* 响应头,观察 HTTP 交换与结论。

警示 · 三个常被误解的点。其一,简单请求(GET / HEAD / 简单 POST)没有预检——浏览器照样把请求发给服务器,只是当 CORS 不通过时拦截 JS 读响应;也就是说副作用可能已经发生,CORS 不是服务器侧的访问控制,真正的鉴权仍要服务器自己做。其二,非简单请求(PUT/DELETE、自定义头、JSON body)先发 OPTIONS 预检,预检不过,真正的请求根本不发。其三,凭据请求下 Allow-Origin: * 无效,必须回显具体 origin。

2 · CSP:文档声明「谁能往我身上加资源」

Content-Security-Policy 换了个轴:who 是子资源的来源(origin / nonce / hash)、what 是资源类型(由 directive 表示:script-src / img-src / connect-src……)、action 是 load / execute。它是一份白名单策略,未声明的 directive 回退到 default-src

图 2-1 · 一份 CSP 与一次资源加载的匹配过程。可编辑三条 directive 的来源列表并切换资源类型与来源,观察哪条 directive 命中、放行还是拦截。

注 · 三个要点。默认回退:script-src 没写就用 default-srcdefault-src 也没写,这类资源就不受限。多份 CSP 等于 deny-overrides:一个响应可带多份 CSP(头 + meta、或多个头),浏览器取交集——每一份都必须放行才放行。Report-Only 等于 dry-run:Content-Security-Policy-Report-Only 不拦截、只上报违规,相当于「permit 但附带 obligation:上报」的审计/演练模式。

3 · 同一个模式的其他头

识别出「谁(origin)· 对什么 · 做什么 → 准/拒」这个骨架后,其余安全头便都易于理解了。

  • Permissions-Policy(前身 Feature-Policy):who 是文档 / iframe 的 origin,what 是强能力(camera / geolocation / fullscreen),action 是使用。语法 geolocation=(self "https://trusted.example") 就是一条放行名单,camera=() 是 deny-all。还能通过 iframe 的 allow 属性委派给子框架。
  • Cross-Origin-Resource-Policy(CORP):资源侧声明「谁能把我当子资源嵌入」(same-origin / same-site / cross-origin)——what 是这个资源,who 是嵌入方 origin。
  • Cross-Origin-Opener-Policy(COOP)/ Embedder-Policy(COEP):一对用来开启跨源隔离的策略,决定本文档与被打开窗口 / 被嵌入资源的关系,换取 SharedArrayBuffer 这类高能力。本质仍是「在什么 origin 关系下,准不准这么干」。
  • Strict-Transport-Security(HSTS):「今后只准用 HTTPS 访问我」——action 是用 http 访问,decision 是强制升级 / 拒。

4 · 一张表收束:HTTP policy 头映回四轴

表 4-1 · 五类 HTTP 策略各自的 who / what / action 与默认决策。principal 全是 origin;判定全在浏览器(PDP+PEP);策略全由服务器响应头声明(PAP)。
策略 who what action 默认 / 组合
Same-Origin Policy 请求方 origin 跨源响应 / DOM read default deny
CORS Origin 头 目标 URL HTTP method 显式 allow 才放宽 SOP
CSP 子资源来源 资源类型 (directive) load / exec 白名单 + default-src 回退;多份取交集(deny-overrides
Permissions-Policy 文档 / iframe origin 强能力 feature use allowlist,可委派
CORP / COOP / COEP 嵌入方 / 打开方 origin 资源 / 文档 embed / open 按 origin 关系判定

建议 · HTTP 的安全头不是一组需要死记硬背的规则,而是同一个授权函数在浏览器中的实例——将 origin 视为 principal,先 default-deny,再用响应头声明的规则放宽或收紧。面对任何一个 *-Policy,先问「它的 who / what / action 是什么、默认是准还是拒、多条如何组合」,便会一目了然。

5 · 参考文献

  1. MDN. Same-origin policy. web 的 default-deny 地基,origin = scheme + host + port。developer.mozilla.org
  2. MDN. Cross-Origin Resource Sharing (CORS). 简单请求 / 预检 / 凭据,以及全部 Access-Control-Allow-* 响应头。developer.mozilla.org
  3. MDN. Content Security Policy (CSP). directive、source list、default-src 回退、report-only。developer.mozilla.org
  4. MDN. Permissions-Policy header. 强能力的 allowlist 与 iframe 委派。developer.mozilla.org