# 隔离 Web 应用（IWA）

> 隔离 Web 应用是什么：经签名的 Web Bundle、isolated-app 协议、它新增的 manifest 字段、被施加的策略，以及可用范围。

隔离 Web 应用（Isolated Web App，IWA）是一种用 Web 技术构建的应用：它被打包进 Web Bundle、由开发者签名后分发给用户，而不是托管在在线 Web 服务器上并经由 HTTPS 获取。[WICG 解释文档](https://github.com/WICG/isolated-web-apps) 把这个模型提给两类应用：威胁模型中包含应用服务器本身被攻破的应用，以及需要普通 Web 安全模型无法授予的能力的应用。

## 工作原理

[解释文档](https://github.com/WICG/isolated-web-apps) 把该提案的核心描述为让应用更新变成显式的。TLS 密钥必须在线可用才能建立新连接，而用于签名 Web Bundle 的密钥可以安全地离线保管、使用频率很低；同时，分发更新的通道又构成一个可以检查新资源是否含有恶意内容的环节。

### 打包与签名

Google 的 ChromeOS 文档 [Isolated Web Apps](https://chromeos.dev/en/web/isolated-web-apps) 指出，隔离 Web 应用的页面与资源不能像普通 Web 应用那样由在线服务器提供或经网络获取，应用的资源要打包成 Signed Web Bundle，即一个把打包资源与完整性块拼接在一起的 `.swbn` 文件。该页把签名密钥说明为 Ed25519 或 ECDSA P-256 密钥对，私钥用于签名 bundle，公钥用于验证 bundle。

同一页面还指出了一个关于架构的结果：站点必须能在离线状态下提供它的每一个页面，因此服务端渲染的页面不能包含进来，可考虑的架构是静态生成或客户端渲染。

### 身份与 isolated-app 协议

解释文档给出了身份不能继续沿用 DNS 名称的理由：域名会因为对域名本身或对验证域名所有权所用基础设施的临时失控而变得脆弱，作者们也希望避免构建一个与证书颁发机构共用同一命名空间的替代体系。因此隔离 Web 应用使用一个新协议，暂定为 `isolated-app://`，其 URL 的 authority 部分基于签名 Web Bundle 所用的公钥。

解释文档还把细节拆进附属文档：[Scheme Explainer](https://github.com/WICG/isolated-web-apps/blob/main/Scheme.md) 描述 `isolated-app:` 协议与签名 Web Bundle ID，[Updates Explainer](https://github.com/WICG/isolated-web-apps/blob/main/Updates.md) 描述 `version`、`update_manifest_url` 清单字段与更新过程。

ChromeOS 页面给出了实践上的结论：因为身份与签名密钥绑定，每个应用都需要自己的密钥，同一应用用不同密钥签名会被当作不同的应用。签名工具会打印出得到的 Web Bundle ID 与来源，该页面还提到隔离 Web 应用像 PWA 一样在自己的窗口中打开、没有地址栏，所以用户不太可能看到那个来源。

### Manifest 字段与更新

ChromeOS 文档指出，隔离 Web 应用的 Web 应用清单不从 HTML 链接，因为它需要在安装时就可被发现：它必须位于 `/.well-known/manifest.webmanifest` 并包含在包内。文档记录了 PWA 所用清单键之外的两个字段：

| 字段 | 状态 | 内容 |
|---|---|---|
| `version` | 隔离 Web 应用必填 | 由一个或多个整数以点分隔组成的字符串，匹配正则 `^(\d+.?)*\d$` |
| `update_manifest_url` | 可选但推荐 | 一个 HTTPS URL（测试时可用 localhost），从那里可以取到 Web 应用更新清单 |

同一页面记录了 `display` 在该模型中的不同行为：`browser` 与 `minimal-ui` 都被强制为 `minimal-ui` 显示，`fullscreen` 与 `standalone` 都被强制为 `standalone` 显示，而额外的 `display_override` 选项按预期工作。关于降级保护，解释文档指出实现可以要求提供 `version` 清单字段，或者要求签名时间戳单调递增。

### 施加在应用上的策略

两份文档都列出了支撑该模型的响应头。[解释文档](https://github.com/WICG/isolated-web-apps) 给出了施加在从 bundle 加载的资源上的内容安全策略，其中 `'self'` 指来自应用 Web Bundle 的资源，并排除 `blob:`、`filesystem:` 等本地协议以及内联脚本。它还给出跨源隔离响应头 `Cross-Origin-Opener-Policy: same-origin`、`Cross-Origin-Embedder-Policy: require-corp`、`Cross-Origin-Resource-Policy: same-origin` 与 `Content-Security-Policy: frame-ancestors 'self'`。

由此还有更多限制。两份文档都指出，为防止顺序破坏（sequence breaking）攻击，跨源导航进入该应用是不允许的，因此应用要通过明确定义的入口打开，例如它的 `start_url`、协议处理器、Share Target 或 launch handler。ChromeOS 页面还指出隔离 Web 应用默认阻止所有权限请求，由开发者通过 `permissions_policy` 清单字段选择性开启；在那里添加一项权限并不等于授予它，而是让该权限变成可以被请求的。

:::security
ChromeOS 页面指出，由于 Web Bundle 也可以携带 Permissions Policy 响应头，只有在清单字段和 bundle 响应头中都声明的权限才被允许，而且只有同时出现在两份允许列表中的来源才被允许（[Isolated Web Apps](https://chromeos.dev/en/web/isolated-web-apps)，chromeos.dev）。清单字段与 bundle 响应头是需要分别保持一致的位置。
:::

## 可用范围

ChromeOS 文档带有一条版本说明 `ChromeOS version: 128`，并指出隔离 Web 应用的首个版本以及任何需要它们的高信任 API，将面向由 Chrome Enterprise 管理的 ChromeOS 设备和部分开发合作伙伴提供，并有意在之后扩展到更多合作伙伴以及非受管设备和跨平台设备。本地测试方面，同一页面要求 Chrome 或 ChromeOS 120 及以上版本，并启用隔离 Web 应用相关标志。

底层的平台概念另有规范：[Isolated Contexts](https://wicg.github.io/isolated-web-apps/isolated-contexts.html) 是由 Web Platform Incubator Community Group 发布的 Draft Community Group Report，它定义了"隔离上下文"，使实现者与规范作者可以只在满足隔离性与完整性的最低标准时才启用某些功能。该文档声明它不是 W3C 标准，也不在 W3C 标准轨道上。

## 示例

下面几段分别覆盖隔离 Web 应用需要的清单、权限字段，以及一个让共享代码在该模型之外也能工作的运行时检测。

### 隔离 Web 应用的最小清单

ChromeOS 页面的最小示例在 PWA 清单已有的键之外加上 `version` 与 `update_manifest_url`，并且该文件随 bundle 发布在 `/.well-known/manifest.webmanifest`，而不是从 HTML 链接。

```json
{
  "name": "IWA Kitchen Sink",
  "version": "0.1.0",
  "update_manifest_url": "https://example.com/updates.json",
  "start_url": "/",
  "icons": [
    { "src": "/images/icon.png", "type": "image/png", "sizes": "512x512", "purpose": "any" },
    { "src": "/images/icon-mask.png", "type": "image/png", "sizes": "512x512", "purpose": "maskable" }
  ]
}
```

Web 应用更新清单是一个描述该应用每个版本的 JSON 文件。[Updates Explainer](https://github.com/WICG/isolated-web-apps/blob/main/Updates.md) 给出的形式是单个 JSON 对象，带一个顶层 `versions` 字段，其值是一个列表；ChromeOS 页面自己的示例用的也是这个键。列表中每一项至少携带 `version` 与 `src`：`version` 是与清单自身 `version` 字段同格式的字符串，`src` 是相对于更新清单所在位置解析的 URL，必须是 HTTPS URL，或测试用的 localhost HTTP URL。解释文档把 `channels` 标为可选——它是一组更新频道标识符，未设置时按 `["default"]` 处理。

### 声明应用可以请求的权限

ChromeOS 页面的车队追踪示例允许应用本身与一个嵌入的地图来源请求地理位置，并允许任意来源请求全屏。

```json
{
  "permissions_policy": {
    "geolocation": ["self", "https://map.example.com"],
    "fullscreen": ["*"]
  }
}
```

由于该字段只是让权限变成可请求而不是已授予，应用仍然要调用普通的权限 API，并仍然要处理被拒绝的情况。没有出现在这个对象里的能力，其请求在到达用户之前就被阻止。

### 在运行时检测该模型

在站点与它的隔离 Web 应用构建之间共享的代码需要知道自己跑在哪一边。协议与跨源隔离都是该模型有文档记载的属性，所以检测读取它们，而不是嗅探 user agent。

```js
function runsInIsolatedAppRuntime() {
  if (!('crossOriginIsolated' in window)) {
    // 没有跨源隔离信号：按普通网页处理。
    return false;
  }
  return window.crossOriginIsolated && location.protocol === 'isolated-app:';
}

if (runsInIsolatedAppRuntime()) {
  document.documentElement.dataset.runtime = 'isolated-app';
} else {
  // 普通网页：高信任 API 不存在，保留走网络的代码路径。
  document.documentElement.dataset.runtime = 'web';
}
```

这个检测报告的是运行环境，而不是应用是怎么分发的。Dev Mode Proxy 安装同样跑在 `isolated-app:` 上、同样适用那套策略，只不过它指向的是开发服务器而不是 bundle，所以它在这里同样得到 `isolated-app`。上面用到的协议与 `crossOriginIsolated` 信号并不区分这两种分发方式，因此需要区分签名 bundle 与 Dev Mode Proxy 安装的代码，得自己带一个标记，比如构建期标志。

用这个结果来选择代码路径，而不是用来假定某个能力存在：一项权限仍然必须同时出现在 `permissions_policy` 字段和 bundle 的响应头中，应用才能请求它。

## 实测行为

开发路径无需企业注册即可走通，下面这些字符串就是文档为它打印出来的内容。

:::observed
[Isolated Web Apps](https://chromeos.dev/en/web/isolated-web-apps)（chromeos.dev）用确切字符串记录了开发流程：启用标志 `chrome://flags/#enable-isolated-web-app-dev-mode`，然后打开 `chrome://web-app-internals`，那里提供两个操作 **Install IWA via Dev Mode Proxy** 与 **Install IWA from Signed Web Bundle**（英文界面）。签名之后，工具把身份打印为：

```text
Web Bundle ID: ggx2sheak3vpmm7vmjqnjwuzx3xwot3vdayrlgnvbkq2mp5lg4daaaic
Isolated Web App Origin: isolated-app://ggx2sheak3vpmm7vmjqnjwuzx3xwot3vdayrlgnvbkq2mp5lg4daaaic/
```
:::

该页面指出，经由 Dev Mode Proxy 安装可以指向任意 URL，包括本地开发服务器，而且不需要打包；以这种方式安装的应用会被分配一个随机标识符。所以开发期间看到的来源并不是签名构建将会拥有的来源。

## 另请参阅

- [可安装性条件](/zh/reference/installation/installability-criteria/)
- [Trusted Web Activity](/zh/reference/installation/twa/)
- [ChromeOS 上的 PWA（启动器与置顶栏）](/zh/reference/platforms/chromeos/)
- [display 清单成员](/zh/reference/manifest/display/)
- [Isolated Web Apps Explainer](https://github.com/WICG/isolated-web-apps)（github.com）
- [Isolated Web Apps Updates Explainer](https://github.com/WICG/isolated-web-apps/blob/main/Updates.md)（github.com）
- [Isolated Web Apps](https://chromeos.dev/en/web/isolated-web-apps)（chromeos.dev）
- [Isolated Contexts](https://wicg.github.io/isolated-web-apps/isolated-contexts.html)（wicg.github.io）