Permissions Policy、浏览器能力与隐私
基于 W3C 与 MDN,说明 Permissions Policy、浏览器能力检查和嵌入功能的隐私边界。
BotBrowser Team
BotBrowser 可以在声明的 context 中重复授权的浏览器旅程并观察可见结果,但不会编写或部署策略、授予权限、创建设备或证明远程业务记录。Permissions Policy 限制文档可以使用哪些浏览器能力。顶层响应设定上限,iframe 的 allow 属性可把指定功能委派给子 origin。这是能力边界,不是用户授权、设备保证或业务完成证明。本文依据 W3C 规范 与 MDN 参考。
TL;DR
使用最小的功能和 origin allowlist;redirect 后检查最终 origin,让响应头和 allow 一致,并用自有 control/candidate fixture 验证。策略允许的 frame 仍可能缺少 API、安全上下文、用户手势、权限、设备或应用确认。BotBrowser 可重复授权的浏览器旅程并观察可见结果,但不会编写或部署策略、授予权限、创建设备或证明远程业务记录。
Contents
策略控制什么
Permissions-Policy: geolocation=(self "https://widget.example") 让顶层 origin 和指定 widget 可以尝试地理位置。iframe 仍需匹配 allow="geolocation",全部祖先 frame 也必须保留委派。redirect 到另一个 scheme、主机或端口会使最终子 origin 不符合。不同功能的默认值不同,应查看当前功能定义。
不要用 allow="*" 省事;它会把功能委派给占用该容器的任意 origin,包括意外 redirect 或嵌套文档。使用明确 allowlist,记录 header 和 markup 的负责人,并在集成结束时删除条目。祖先响应删除的功能,子文档不能自行恢复。
策略合格不等于权限状态。navigator.permissions 显示 prompt 或 granted 时,frame 仍可能被策略阻止;策略合格的文档也可能遇到拒绝、timeout、设备不可用或浏览器特定错误。报告第一个失败阶段,不要统称为策略失败。
分开记录能力阶段
| 阶段 | 应记录的证据 | 通过不能证明 |
|---|---|---|
| 最终 origin 与上下文 | 最终 URL、scheme、端口、安全上下文 | API 已实现 |
| API 能力 | 例如 typeof navigator.geolocation.getCurrentPosition | 请求一定允许 |
| Permissions Policy | 响应头、allow、祖先链、子 origin | 用户同意或设备存在 |
| 用户/平台决定 | 权限结果、管理员设置、设备状态 | 应用接收了数据 |
| 应用结果 | 有界等待内的可见状态或自有确认 | 远程记录已提交 |
嵌套 frame 也要保持这一区分。让子页面通过自有测试页报告最终 origin 和状态,不要从父页面推断。测试使用稳定的合成权限,不采集真实坐标、媒体、凭据或账户内容。
区分相关策略
CSP 决定脚本、连接、资源和 frame 能否加载;COOP 调整顶层 browsing context 的关系;COEP 规定嵌入跨 origin 资源的条件。它们都不会委派地理位置或摄像头,Permissions Policy 也不会创建跨源隔离或满足 CORS/CORP。
先检查网络和 CSP,再检查最终 origin 及所需的 COOP/COEP,然后检查 Permissions Policy,最后才请求需要用户参与的功能。网络错误、策略阻止、用户拒绝和应用错误有不同负责人。任何浏览器机制都不替代服务端授权、同意体验、输入校验或设备控制。
使用可复现 fixture
在自有 host 提供 /fixtures/permissions-policy/control.html 和 candidate.html。保持 frame origin、按钮、状态标记和路由相同;candidate 带建议 header,control 刻意省略。子页面仅在明确点击后报告 policy=blocked 或 policy=allowed。这两个字符串只是应用标记,必须同时断言实际发送的 Permissions-Policy、iframe 的 allow 和最终子 origin。
import { test, expect } from '@playwright/test';
const cases = [
{ name: 'control', url: 'https://qa.example.test/fixtures/permissions-policy/control.html', expected: 'blocked' },
{ name: 'candidate', url: 'https://qa.example.test/fixtures/permissions-policy/candidate.html', expected: 'allowed' },
];
for (const scenario of cases) {
test(`Permissions Policy ${scenario.name}`, async ({ page }) => {
let response;
try {
response = await page.goto(scenario.url, { waitUntil: 'domcontentloaded', timeout: 8000 });
} catch (error) {
throw new Error(`NETWORK_ERROR:策略断言前导航失败:${error.message}`);
}
expect(response?.ok(), `${scenario.name} HTTP 响应`).toBeTruthy();
await page.getByRole('button', { name: 'Request location' }).click();
await expect(page.getByTestId('policy-result')).toHaveText(new RegExp(`^${scenario.expected}$`), { timeout: 5000 });
});
}
DNS、证书、HTTP 和路由错误必须保留为 NETWORK_ERROR,不能伪装成 blocked。timeout 只表示 fixture 在窗口内没有证据,不表示浏览器拒绝。teardown 时关闭 context 并清理临时数据。fixture 只证明声明的浏览器和路由,不是普遍兼容性或隐私承诺。
隐私处理应只使用合成权限和短期测试值,不记录坐标、媒体、cookie 或账户内容。失败归属按第一个证据区分:网络与 header 交给基础设施,策略链交给嵌入负责人,用户拒绝交给同意流程,API 成功后的保存错误交给应用负责人。变更时保留旧 header、markup 和浏览器版本;回滚后用相同 route、权限状态和断言重跑,确保结果可复现。
结论
嵌入合同应写明顶层 origin、最终子 origin、功能、目的、用户动作、fallback 和负责人。供应商主机、redirect、浏览器版本或嵌套 frame 变化后重新运行 fixture。报告只保留策略值、origin、浏览器版本、状态标记和可见结果。
BotBrowser 可以固定声明的 browser context,重复授权的 control/candidate 旅程。它不会改变网站发送的 Permissions-Policy、修改 iframe origin、授予用户权限、提供摄像头或麦克风、覆盖 CSP/COOP/COEP、修复供应商响应,也不能证明远程业务已提交。实际响应和服务端记录才是权威。参阅 CSP 指南 和 跨源隔离指南。
来源
- W3C: Permissions Policy
- MDN: Permissions Policy
- MDN: iframe
allow - MDN: CSP
- MDN: COOP
- MDN: COEP
- BotBrowser:高级功能
BotBrowser 团队