硅基俊宁.
← 返回文章

10-安全、存储与身份边界:谁能代表哪个站点、读到哪份数据

俊宁 · 日期未详
文章目录

从源码阅读入口、进程边界与验证条件理解安全、存储与身份边界:谁能代表哪个站点、读到哪份数据。

本文讨论通用机制和设计判断;示意不对应任何具体产品的已交付状态。源码阅读坐标沿用原稿,本次整理不构成新的运行时验收。

Chromium 安全、存储与身份边界封面

阅读坐标沿用原稿:Chromium 153.0.8010.37,上游 commit b75a5a95ea1a1b55bdbfd6d9f42d47be7507fb8b。本次同步未重新进行源码与目标平台运行时验收。

站点身份、进程隔离、存储与权限的联合边界

一句话结论

Chromium 中“能访问什么”由 site/process、sandbox、StoragePartition、网络身份和 permission 共同决定;任何单一布尔权限都不能代表完整授权,更不能把网页、Extension、Profile 与本机能力的身份压成同一个 caller。

浏览器安全不是一把“sandbox 开关”。一次网页访问摄像头、读取 cookie、加载跨站 iframe 或调用 Browser Native API 时,至少同时涉及执行进程、站点/文档身份、网络与存储归属、用户权限和 Browser 对 child process 的能力约束。它们分别阻止不同类型的越界;任何一层都不能替其他层背书。

读安全相关源码时,不要先问“这个 API 有没有权限”,而要按顺序问:请求来自哪个 document/frame?它归哪个 site 和 Profile?数据落在哪个 Storage Partition/NetworkContext?Browser 是否允许该进程或 caller 执行这项能力?请求在导航、嵌入或撤销后是否仍成立?

五层边界共同决定一次访问

Web origin / document / frame identity
  -> SiteInstance / RenderFrameHost / renderer process assignment
  -> OS sandbox + ChildProcessSecurityPolicy 的进程能力限制
  -> BrowserContext / StoragePartition / NetworkContext 的数据归属
  -> permission、content setting、用户手势与产品 policy
  -> 受限 API、网络请求、cookie 或设备操作

这是一张阅读图,而非严格的同步调用栈。例如 cookie 由 Network Service 处理,权限 prompt 常在 Browser/product 层完成,site process 选择发生在导航与 RenderFrameHost 管理中。它的价值在于避免常见错误:把“同一 Renderer process”当作“可共享登录态”,把“用户曾经允许过”当作“当前 frame 永远可用”,或把“文件已被授予”当作“任意 Renderer 都可读”。

Site Isolation:进程归属围绕 site,不是每个页面各一个进程

SiteInstance、SiteInfo、SiteInstanceGroup、RenderFrameHost 和 renderer process assignment 共同描述浏览内容在 Browser 侧的站点与进程归属。导航到不同 site、嵌入跨站 frame、发生 process swap 或恢复页面时,Browser 会据此选择、复用或替换合适的 frame/process;具体策略受浏览器模式和 feature 配置影响。

概念解决的问题不能推出的结论
originWeb 安全模型中的 scheme/host/port 身份两个相同 origin 的页面必然共享所有存储或进程
site / SiteInfoBrowser 进程分配与隔离语义的关键输入一个 site 永远只有一个 Renderer 或绝不发生切换
SiteInstance将 frame 与 site/process 选择联系起来每个 tab、frame 或 document 都必有独占 SiteInstance
RenderFrameHostBrowser 对具体 frame/document 生命周期的控制点它代表的 document 在导航后仍有效

Site Isolation 将跨站内容的隔离与权限判断纳入 Browser-owned process/frame 模型,但不承诺“iframe 一律另起进程”。实际排查时应记录目标 frame 的 site、当前/候选 RenderFrameHost、Renderer process id、是否为主 frame、是否跨站导航和 feature 配置。只看 OS 进程数量,无法证明站点隔离是否按预期生效。

OS sandbox 与 ChildProcessSecurityPolicy:两种不同的能力限制

Renderer、GPU、Utility 等 child process 的 OS sandbox 限制系统调用、文件、设备或其他本机资源的可达范围,具体策略与平台相关。它是“进程即使被攻破,仍应被操作系统约束”的防线。

Browser 内的 ChildProcessSecurityPolicy 则记录和检查 Browser 授予 child process 的特定能力,例如可读文件、isolated file system、可 request/commit 的 origin/scheme、WebUI bindings 等。content/public/browser/child_process_security_policy.h 与 content/browser/security/cpsp/child_process_security_policy_impl.* 展示了 Grant* 和 Can* 两类入口。

两者不能混为一谈:

防线主要约束谁典型问题
OS sandbox已运行的 child process 对操作系统资源的直接操作被攻陷 Renderer 能否随意读本机文件或打开设备?
ChildProcessSecurityPolicyBrowser 授给 child process 的 Chromium 内能力此 process 是否被授权读该文件、访问该 origin、使用某类 binding?
API/permission policy当前 document、用户、Profile 与产品策略当前请求是否应被展示 prompt、允许、拒绝或取消?

安全 review 不能只看到一处 GrantReadFile() 就结束:还要追 grant 的触发条件、绑定的 child/frame、导航/关闭后是否回收,以及 OS sandbox 是否仍是最后防线。反过来,也不能以“Renderer 在 sandbox”作为在 Browser handler 中省略 caller/target 校验的理由。

Storage Partition:数据归属不是一个目录名

BrowserContext/Profile 可以拥有一个或多个 StoragePartition。它将一组网络和 Web 存储相关状态组织到可分隔的上下文中,例如 NetworkContext、cookie、HTTP cache、service worker、code cache 等;具体数据类型和生命周期需以当前实现与配置核对。

基线的 StoragePartition 接口提供 GetNetworkContext()、Browser-process CookieManager,以及按 origin/bucket/范围清理数据的入口;StoragePartitionImpl::ClearData* 说明“清理浏览数据”不是简单删除某一个目录。清理操作可能涉及多类后端、异步回调和过滤条件,测试必须等待实际 completion,而不是只确认 UI 点击成功。

Profile / BrowserContext
  -> StoragePartition
     -> NetworkContext
        -> CookieManager、HTTP cache、URLLoaderFactory 等网络状态
     -> 其他按 partition/origin/bucket 管理的 Web 数据

这条 ownership 链解释两个产品事实:normal 与 OTR/incognito 或不同 partition 不应仅因 URL 相同就默认共用登录态;同一个 Profile 下也不应把“当前页面能访问”误写成“所有 Browser Native/Extension 调用都可读取全部数据”。实际是否分区、如何持久化、何时销毁,仍由具体 BrowserContext、StoragePartitionConfig、feature 和产品 policy 决定。

Cookie:网络身份状态有自己的访问规则

cookie 是网络状态的一部分,却不是“任意 Browser 代码都能读写的字典”。在 Network Service 中,NetworkContext 持有 CookieManager,并可建立 RestrictedCookieManager;services/network/cookie_manager.* 和 network_context.* 是相应的服务入口。Browser 侧可通过 StoragePartition 获取受控 manager,但必须尊重 current settings、request context 与调用者身份。

问题不能用的简化正确的核对方向
为什么请求没带 cookie?“URL 一样就该有登录态”partition/context、cookie attributes、site relationship、content settings、请求 credentials 与 policy
为什么页面脚本读不到 cookie?“Browser 里有 CookieManager,所以 JS 都可读”HttpOnly、origin、document/frame、Web API 与 Browser manager 的不同权限
为什么清理后仍有状态?“删了 cookie 就等于所有站点数据清空”清理 mask/filter/completion、cache、storage、service worker、session/账号状态
为什么无痕或嵌入页面行为不同?“同一 Profile 名称就是同一身份”实际 BrowserContext、StoragePartitionConfig、OTR 和嵌入上下文

第三方 cookie、partitioned cookie、First-Party Sets 与相关实验会进一步改变可见性和发送条件。文章或代码不能把某一版本的默认策略写成永恒规则;要在目标版本采集 request、cookie access reason 和 NetworkContext/setting 证据。

Permission:允许的是当前受限请求,不是一张永久通行证

PermissionController 处理当前 document/worker 的权限状态、请求、订阅和重置;PermissionControllerImpl 从 BrowserContext 取得对应实现,并将请求交给 delegate/product policy。公开接口中区分了当前 document、worker、origin without context、embedded requester 等场景,这正是它不能被概括成一张 origin -> allow 表的原因。

一次权限判断的最小输入应包括:

permission type
  + requesting document/frame/worker
  + embedding/top-level context
  + origin/site 与当前 navigation 状态
  + Profile、content setting、enterprise policy
  + 用户手势、prompt 与用户选择

请求到达或 prompt 返回时,document 可能已经导航、frame 已销毁,用户也可能刚刚撤销权限。因此回调必须重新关联有效 caller;“先查一次 allow,后面异步直接使用”会造成向旧页面授予结果或绕过撤销。对设备、屏幕捕获、文件、剪贴板、Browser Native API 等高风险能力,产品还应额外定义明确用户确认和审计语义,不能只复用一个宽泛的 permission boolean。

本文已核验的源码锚点

主题源码入口
site/process 模型content/public/browser/site_instance.h、content/browser/site_instance_impl.*、site_info.*、render_frame_host_manager.*
child process 能力策略content/public/browser/child_process_security_policy.h、content/browser/security/cpsp/child_process_security_policy_impl.*
partition 与清理content/public/browser/storage_partition.h、content/browser/storage_partition_impl.*
网络/cookie 服务services/network/network_context.*、cookie_manager.*、components/content_settings/
页面权限content/public/browser/permission_controller.h、content/browser/permissions/permission_controller_impl.*

运行时验证路径

威胁推演:为什么“当前 Tab 已授权”仍然不够

Tab 是产品 UI 容器,不是稳定安全主体。一个 Tab 内可以有跨站 iframe、发生 navigation、进入 BFCache、替换 RenderFrameHost,甚至在同一 Tab ID 下变成完全不同的 document。能力若只记录 Tab ID 或 URL 字符串,晚到 reply 很可能交给错误页面;若只记录 origin,又可能忽略 Profile/StoragePartition、frame 和用户手势。

最低限度的授权记录应能回答:哪个 Profile/partition、哪个 Extension 或 WebUI caller、哪个 frame/document、哪个 origin/site、哪次用户动作、哪项 capability、有效到何时、如何撤销。Browser 侧用自己持有的对象和 token 复核,Runtime 只消费经裁决后的最小参数,不自行推断网页身份。

一次访问同时携带多种身份

安全问题经常源于把不同身份压成一个 ID。一次 Browser capability 调用可能同时具有:Profile/BrowserContext、Extension 或 WebUI caller、WebContents、frame、document、origin/site、StoragePartition、network isolation identity 和 user gesture。它们回答不同问题,不能用 URL 或 Tab ID 代替全部。

身份主要回答失效时机示例
Profile/BrowserContext属于哪个用户环境与策略域Profile shutdown、OTR 会话结束
Extension/WebUI identity谁在调用产品能力扩展卸载/禁用、页面销毁
frame/document结果属于哪一次页面生命周期navigation commit、frame detach
origin/siteWeb 权限与进程隔离如何判断跨 origin 导航、sandboxed origin
StoragePartition使用哪组网络与站点数据上下文partition/config 改变、context 销毁
user activation用户是否在有效时间内主动触发activation 消费或过期

安全校验应贴近需要它的动作。绑定连接时冻结 caller context,可以避免每次从可伪造参数重建身份;执行敏感动作前再检查当前 document、permission 和目标状态,可以防止连接建立后发生导航或撤权。

Site、Origin、Process 三者不能互换

Origin 通常由 scheme、host、port 等决定 Web 安全边界;site 用于 Site Isolation 等更粗粒度的进程归属判断;process 是承载执行的 OS 容器。同进程中的两个 context 不因此同 origin,跨进程的两个 frame 也可能属于同一更高层 site 关系。

因此“Renderer 已隔离”只是纵深防御的一层。Browser 接收 IPC 后仍要验证该 RenderProcessHost/RenderFrameHost 是否被允许访问目标资源;Renderer 发来的 origin 字符串不能单独作为信任依据。反过来,把每个敏感动作都简化为“不同进程就安全”,会遗漏同进程多 frame 与被攻陷 Renderer 的威胁模型。

存储隔离不只发生在磁盘目录

Cookie、HTTP cache、DOM storage、IndexedDB、service worker、code cache 和 permission state 有不同 owner 与 key。StoragePartition 将多类上下文组织在一起,但具体访问仍受 origin、top-level site、partition key、cookie policy 和 Profile/OTR 状态影响。

Off-the-Record 也不是给普通 Profile 换一个路径。它要求生命周期结束后不留下不该持久化的数据,并隔离网络、存储与 Extension 行为。验证隐私模式时应检查磁盘残留、共享 service、download/外部应用交接和崩溃恢复,而不是只确认 history UI 为空。

Permission 的授权对象必须足够具体

摄像头、位置、通知、剪贴板、文件与屏幕捕获的授权语义不同。有的绑定 origin,有的要求 transient user activation,有的每次由系统 picker 返回一个受限对象,还有的同时受 OS permission 控制。统一弹窗组件不代表它们共享授权模型。

一项 permission flow 至少需要回答:谁请求、代表哪个 frame/document、请求哪种资源、用户看到什么、授权保存多久、如何撤销、导航/进程退出后怎样处理、受管理 policy 是否覆盖。只保存 allow=true 会丢掉决定安全性的上下文。

用威胁推演检查 Browser capability

可以主动构造四个攻击/故障场景:

  1. content script 在请求后立刻导航,让旧 reply 尝试写入新 document;
  2. 恶意页面伪造 Extension bridge 消息,试图调用 Browser Native API;
  3. 同一 Profile 的另一个 Tab 猜中 request id,读取不属于它的结果;
  4. OTR 与普通 Profile 同时打开相同站点,观察缓存、Runtime session 或日志是否串联。

通过条件包括高权限动作没有发生、敏感数据没有进入错误身份域、审计日志仍能定位 deny 原因且不泄露 payload;接口返回预期错误只能证明其中一部分。这样安全测试才能与产品可用性同时成立。

Deny path 也必须是产品 contract

安全拒绝不应只返回通用 false。调用方需要区分 caller 无权限、document 已失效、用户拒绝、Policy 禁止、OS 拒绝和资源不可用,才能给出正确反馈且避免无意义重试。错误分类可以稳定,内部细节和敏感策略不必全部暴露给页面;Browser 日志保留脱敏审计,UI 只展示用户可行动的信息。

Blob、data、filesystem 等非普通 URL 还要追溯创建者与继承的 origin,不能用当前显示字符串重新猜身份。

工程结论

Chromium 安全模型是一条从站点身份到本机能力的连续约束,把 Renderer 放进 sandbox 只完成其中一层。新增 AI 能力越强,越需要把 caller、数据范围、生命周期和审计写进 contract,不能依赖调用链“来自自家代码”。

继续阅读

上游源码入口

文中的相对源码路径可从 Chromium 官方上游快照 定位。此入口保留原稿的上游阅读坐标,不包含任何产品定制代码,也不替代运行时验收。