Isolated Web Apps(IWA)解説:署名付きWeb Bundleの仕組みとunframed時計デモ検証
1. はじめに
Web の最大の強みは URL を開くだけで即座にアクセスできる手軽さですが、この「URL を踏んだだけで誰でも到達できる(drive-by web)」という特性はセキュリティ上の大きな制約でもあります。生の TCP / UDP 通信を行う Direct Sockets API、Web コンテンツを高度に制御して埋め込む Controlled Frame API、OS のウィンドウ枠を消し去る Unframed display mode といった強力な機能を、通常の Web ページで安全に開放することは困難です。
また、企業や開発者からは「実行時にリモートサーバーを信頼したくない」「暗号学的に署名されたスタンドアロンパッケージとしてバージョン管理・配布したい」というニーズも存在します。こうした背景から登場したのが、Web プラットフォーム上に「隔離・バンドル・バージョン管理・署名・信頼」されたアプリケーションモデルを構築する Isolated Web Apps(IWA) です。
本記事では、ChromeOS および Chrome における IWA のアーキテクチャと「Secure by design」の 3 本柱、更新・配布の仕組みを整理します。後半では、背景透過を前提にデザインしたデスクトップ時計「Glass Clock」を実際に IWA としてビルド・署名・インストールし、unframed 表示モードやウィンドウ透過の実挙動を検証します。
2. IWA とは
IWA は従来の Web サイトや PWA を置き換えるものではなく、Web における「信頼のスペクトラム(Spectrum of trust)」の右端を担う選択肢です。なお、通常の Web(https://)と IWA(isolated-app://)はオリジンや配信方式に互換性がなく、後から相互に移行することはできません。
| 比較項目 | Drive-by Web(通常の Web) | インストール型 PWA | Isolated Web Apps(IWA) |
|---|---|---|---|
| 配信とレンダリング | HTTPS で都度取得(SSR 可) | HTTPS + SW キャッシュ(SSR 可) | Signed Web Bundle(.swbn)のみ(SSR 不可・SSG/CSR 前提) |
| オリジンと同一性証明 | https://<domain>(DNS / TLS 証明書) | https://<domain>(DNS / TLS 証明書) | isolated-app://<Web Bundle ID>(オフライン秘密鍵の電子署名) |
| Manifest の配置 | 任意(<link rel="manifest">) | 任意(<link rel="manifest">) | /.well-known/manifest.webmanifest に固定 |
| CSP / 分離ポリシー | 開発者が任意に設定 | 開発者が任意に設定 | ブラウザが厳格な CSP と Cross-Origin Isolation を強制 |
| 権限の既定値 | 実行時にプロンプト要求可 | 実行時にプロンプト要求可 | 既定で全ブロック(permissions_policy で明示オプトイン) |
| 利用できる高度 API | 標準 Web API のみ | ファイル関連付け等の拡張 | Direct Sockets / Controlled Frame / Unframed 等 |
Chrome 公式ドキュメントでは、リーチの広さや鍵管理の負担、デプロイの柔軟性から「要件を満たせる限り、可能な限り最も低い信頼モデル(通常の Web や PWA)をデフォルトとして選ぶべき」と強く推奨されています。IWA は専用の高信頼 API や厳格なオフライン署名モデルが本当に必要なケースに絞って採用します。
現在の提供状況は以下の通りです。
- サポート対象:初期リリースで正式サポートされているのは Chrome Enterprise 管理下の ChromeOS デバイスおよび一部の開発パートナー環境です(非管理デバイスや Windows / macOS / Linux への本格拡大は将来予定。なお Chrome Enterprise ヘルプには Windows 等で
IsolatedWebAppInstallForceListポリシーにより強制インストールできる旨の記載があります)。 - 対応バージョン:Chrome / ChromeOS 120 以降で開発者モードでのテストが可能になり、ChromeOS 128 で初期リリースされました。
- IWA allowlist 制限:Chrome 143 以降(ChromeOS)、管理コンソール経由でインストール・更新できるのは審査・登録された「IWA allowlist」掲載アプリのみに制限されています(開発者モードであれば自作アプリのテストは引き続き可能です)。
3. Secure by design の 3 本柱
IWA が高信頼 API を安全に扱えるのは、Packaged・Isolated・Locked down の 3 本柱によって設計段階からの安全性(Secure by design)を強制しているためです。
3-1. Packaged:Signed Web Bundle と公開鍵ベースのオリジン
IWA ではライブサーバーから HTML や JS を配信することはできず、全リソースを 1 つの Signed Web Bundle(.swbn) にまとめる必要があります。すべてのページをバンドル内からオフライン配信するため SSR は利用できず、SSG または CSR(SPA)が基本となります。
通常の Web の TLS 秘密鍵はオンラインサーバー上に置く必要がありますが、.swbn(Web Bundle .wbn の先頭に署名入りの integrity block を連結した形式)の署名鍵はオフライン環境で安全に保管でき、サーバー侵害によるコード改ざんや DNS ハイジャックのリスクを排除できます。
アプリの識別子にはドメインを使わず、署名鍵の公開鍵から Base32 エンコードで導出される Web Bundle ID と isolated-app:// スキーム(例:isolated-app://ggx2sheak3vpmm7vmjqnjwuzx3xwot3vdayrlgnvbkq2mp5lg4daaaic/)が使われます。
署名鍵には、SSH の Ed25519 鍵と同様に短く高速で安全な Ed25519 が扱いやすい選択肢です。OpenSSL で鍵を生成・暗号化し、dist/ ディレクトリの静的ファイルから wbn と wbn-sign で .swbn を作成する手順は以下の通りです(パスフレーズは WEB_BUNDLE_SIGNING_PASSPHRASE 環境変数でも渡せます)。
# 1. Ed25519 秘密鍵を生成し、PKCS#8 形式でパスフレーズ暗号化して平文鍵を削除
openssl genpkey -algorithm Ed25519 -out private_key.pem
openssl pkcs8 -in private_key.pem -topk8 -out encrypted_key.pem
rm private_key.pem
# 2. dist/ から未署名バンドル (out.wbn) を作り、秘密鍵で署名して signed.swbn を生成
npx wbn --dir dist -o out.wbn
npx wbn-sign -i out.wbn -k encrypted_key.pem -o signed.swbn3-2. Isolated:オリジン分離・強制 CSP・ディープリンク制限
IWA はブラウザの通常タブで開くことはできず、必ずスタンドアロンウィンドウ(独立ウィンドウ)として実行されます。isolated-app://<Web Bundle ID> オリジンで動作するため、自社サイト(https://)や他の IWA とも同一オリジンポリシーによって Cookie や localStorage 等のストレージが完全に分離されます。
さらに、通常は開発者がセキュリティヘッダーとして任意に設定する CSP や Cross-Origin Isolation が、IWA ではブラウザ側で無条件に強制適用されます。開発時に特に影響する制約は以下の 4 点です。
- インラインスクリプトと外部 JS の禁止:
script-src 'self' 'wasm-unsafe-eval'が強制され、ここでの'self'は Signed Web Bundle 内のリソースのみを指します。外部 CDN のスクリプト、HTML 内のインライン<script>、eval()はすべて禁止です(Wasm は実行可能)。 - Trusted Types の強制:
require-trusted-types-for 'script'により、element.innerHTML = ...等に生文字列を代入するとエラーになります(textContentや DOM API を使うか、Trusted Types ポリシーを通す必要があります)。 - 外部フォント URL の禁止:
font-src 'self' blob: data:のため、Google Fonts 等を外部https:から直接読み込むことはできません。フォントはバンドルに同梱します。 - COEP による外部リソースの CORP / CORS 要件:
Cross-Origin-Opener-Policy: same-originやCross-Origin-Embedder-Policy: require-corp等が強制されるため、外部 HTTPS の画像やメディアを読み込む場合も配信元サーバー側で適切な CORP または CORS ヘッダーが必要です。
また、想定外の順序で内部ページへ直接遷移させるシーケンスブレイキング攻撃を防ぐため、外部からの任意 URL へのディープリンクは禁止されています。外部から起動・遷移できるのは Manifest で定義した start_url、protocol_handlers、share_target、launch_handler のみに制限されます。
3-3. Locked down:permissions_policy による権限のオプトイン
IWA は既定ですべての権限リクエストをブロックします。位置情報やフルスクリーンなどの権限を使用したい場合は、トップレベルおよび子フレーム(iframe や Controlled Frame)が使う権限を Web App Manifest の permissions_policy に明示してオプトインする必要があります。
{
"name": "Fleet Tracker IWA",
"permissions_policy": {
"geolocation": ["self", "https://map.example.com"],
"fullscreen": ["*"]
}
}ここに記述してもインストール時に権限が自動付与されるわけではなく、あくまで「実行時に権限をリクエストすることが許可される(ブロックが解除される)」状態になります。また、Web Bundle 内の HTML レスポンスに Permissions-Policy ヘッダーも含めた場合は両者の積集合(両方で許可されたディレクティブとオリジン)のみが有効となり、インストール前に要求権限を監査できる仕組みになっています。
4. バージョン管理と配布
4-1. マニフェストの固定配置と Update Manifest
IWA では HTML から <link rel="manifest"> でリンクせず、バンドル内の /.well-known/manifest.webmanifest という固定パスにマニフェストを配置します。通常の PWA と異なり version フィールドが必須(1.0.0 のようなドット区切りの整数列)です。また、display は browser を指定しても minimal-ui に強制され、standalone や display_override(unframed 等)が利用できます。
アップデートを配信するには、マニフェストの update_manifest_url が指す HTTPS サーバー上に Update Manifest(JSON) と新しい .swbn を配置します。
{
"channels": {
"next": { "name": "Next / Beta Channel" }
},
"versions": [
{
"version": "5.3.0",
"src": "https://example.com/iwa/app-5.3.0.swbn",
"channels": ["next", "default"]
},
{
"version": "5.3.1",
"src": "https://example.com/iwa/app-5.3.1.swbn",
"channels": ["next"]
}
]
}端末は既定で default チャンネルを購読するため上記では 5.3.0 が適用され、next チャンネルを購読する端末にのみ先行して 5.3.1 が配信されます。インストール済みの IWA はバックグラウンドで 4〜6 時間ごとに更新チェックを行います(chrome://web-app-internals から手動更新も可能です)。
4-2. Google 管理コンソールでの配布と IWA 専用 API
Chrome Enterprise 環境では、Google 管理コンソールの [デバイス]>[Chrome]>[アプリと拡張機能]>[ユーザーとブラウザ] から対象 OU を選び、右下の「+」ボタンの 「Add an Isolated Web App」 に Web Bundle ID と Update Manifest URL を登録して配布します。インストールポリシー(強制インストール、シェルフ固定、ブロック)やログイン時の自動起動(閉じられないキオスク風稼働を含む)も設定可能です。また、管理コンソールの [Web capabilities] に Web Bundle ID を登録すれば、Window management や Local font access、Screen monitoring といった権限をダイアログなしで管理者側から自動付与(allowed)できます。
IWA で利用できる代表的な高度 API と Chrome 152 の新機能は以下の通りです。
- Direct Sockets API:任意のホスト・ポートに対して生の TCP / UDP ソケット通信を直接行う API。
- Controlled Frame API:任意の Web コンテンツを埋め込み、ナビゲーション制御やスクリプト注入を行える
<webview>相当の API。 - Sub apps(Chrome 152):1 つの IWA 配下に複数のサブアプリを登録・管理できる仕組み。
- Unframed display mode(Chrome 152):OS のウィンドウ枠やタイトルバーを消し、Web コンテンツでウィンドウ全体を占有する
display_overrideの表示モード(Window Management 権限が必須)。 window-dragCSS プロパティ(Chrome 152):従来の-webkit-app-region: dragを標準化・改名したもので、move(ドラッグ移動)またはnone(クリック操作対象)を指定してドラッグ領域を定義するプロパティ。
5. デモ:背景透過の時計「Glass Clock」
ここからは、Chrome 152 の unframed 表示モードと window-drag を試すため、文字盤の背景を持たないアナログ+デジタル時計「Glass Clock」を IWA として作成し、OS のデスクトップ上でもタイトルバーが消えて背景が透過するかを検証します。
5-1. 構成とマニフェスト・実装のポイント
リポジトリ内では、アプリ本体は public/demos/iwa-clock/、署名ツールは demos/iwa-transparent-clock/ に配置しています(.gitignore に keys/ と dist/ を指定し、秘密鍵のコミットを防止しています)。
public/demos/iwa-clock/.well-known/manifest.webmanifest の要点は以下の通りです。display_override で "unframed" を最優先に指定し、それに必要な "window-management" 権限を permissions_policy でオプトインしています。
{
"name": "Glass Clock",
"version": "1.0.2",
"display": "standalone",
"display_override": ["unframed", "borderless", "standalone"],
"permissions_policy": {
"window-management": ["self"]
}
}CSS(style.css)では、タイトルバーのない unframed ウィンドウでも移動できるよう時計全体にドラッグ領域を指定し、右上の操作ボタン(文字色切替・閉じる)だけをドラッグ対象から外しています。また、IWA 起動時用の背景を @media (display-mode: ...) で定義しています。
.clock {
app-region: drag;
-webkit-app-region: drag;
window-drag: move;
}
.controls {
app-region: no-drag;
-webkit-app-region: no-drag;
window-drag: none;
}
@media (display-mode: standalone), (display-mode: unframed), (display-mode: borderless) {
html {
background: radial-gradient(120% 120% at 30% 20%, #2a2e6b 0%, #16181d 70%);
}
}JavaScript(clock.js)は外部モジュールとして読み込み、以下の 2 点で強制 CSP に対応しています。
innerHTML不使用で Trusted Types を回避:デジタル時刻の更新にはtextContent、針の描画には Canvas 2D API のみを使い、require-trusted-types-for 'script'のエラーを回避しています。- Canvas を毎フレーム透明クリア:
ctx.clearRect()で Canvas 全体を透明に戻してから目盛りと針だけを描画し、文字盤の塗りつぶしを行いません。
5-2. ビルドと Chrome へのインストール手順
demos/iwa-transparent-clock/ では、wbn(0.0.10)と wbn-sign(0.3.3、Node 22.13 以降が必要)を使ってバンドル内の URL を "/" 起点で登録し、.swbn を出力します。
cd demos/iwa-transparent-clock
npm install && npm run keygen && npm run buildSigned Web Bundle : dist/glass-clock.swbn
Web Bundle ID : 3nuzhl3u4jgrf5reo6ph3z4miarck7qo3jj7os6wfxkgmnigjuqqaaic
IWA Origin : isolated-app://3nuzhl3u4jgrf5reo6ph3z4miarck7qo3jj7os6wfxkgmnigjuqqaaic/生成した glass-clock.swbn を Chrome にインストールする GUI 手順は以下の 3 ステップです。
chrome://flags/#enable-isolated-web-app-dev-modeを開き、「Isolated Web App Developer Mode」を Enabled にして Chrome を再起動します。chrome://web-app-internalsを開き、「Install IWA from Signed Web Bundle」 からdist/glass-clock.swbnを選択してインストールします。- インストールされた「Glass Clock」のアプリ設定(または権限プロンプト)で、「ウィンドウ管理(Window management)」権限を「許可」に切り替えます。
5-3. 検証結果
2026年11月1日、非管理端末の macOS 上の Google Chrome 156.0.8078.80 で検証したスクリーンショットが以下です。
実機検証で確認できた挙動は以下の通りです。
| 検証項目 | 結果と詳細 |
|---|---|
| インストールとオリジン分離 | 成功。isolated-app://3nuzhl3u... オリジンの独立ウィンドウで起動し、Finder の「Chrome アプリ」にも登録された。 |
unframed によるタイトルバー消去 | 成功。Window management 権限の許可後に matchMedia('(display-mode: unframed)').matches が true となり、上部のタイトルバーが完全に消えた(macOS の赤・黄・緑ボタンのみ残る)。 |
| OS レベルのウィンドウ背景透過 | 不可。CSS と Canvas を透明にしても Chrome のネイティブウィンドウ層が背面を alpha = 1.0 の完全不透明な白で塗りつぶしてしまう。Manifest の background_color を "transparent" や "#00000000" にしても変化なし。 |
| ドラッグ移動とボタンクリック | 未確認。今回の検証はスクリーンショットと DevTools Protocol による自動確認で行ったため、実際のドラッグ移動やボタンのクリック操作は確かめていない。 |
| 開発者モードフラグなしでの起動 | 不可。インストール後に IsolatedWebAppDevMode フラグを外して起動し直すと、中身が一切描画されず空のウィンドウになった。 |
6. IWA を選ぶべきケース
ここまでの仕様と検証結果を踏まえ、IWA を選ぶべきケースと代替案を整理します。
| 判断の軸 | IWA を選ぶべきケース | IWA を選ぶべきでないケース(代替案) |
|---|---|---|
| 利用したい API | Direct Sockets(TCP / UDP 直接通信)、Controlled Frame、Unframed ウィンドウなど IWA 専用の高信頼 API が必須。 | 標準 Web API や通常の PWA の機能だけで要件を満たせる → 通常の Web / PWA。 |
| 配布先と運用モデル | Chrome Enterprise 管理下の ChromeOS / Chrome 向けに、オフライン署名された .swbn を管理コンソールで統制配布したい。 | 一般コンシューマーに URL で広く届けたい、または SSR やサーバー側での即時デプロイが不可欠 → 通常の Web / PWA。 |
| ウィンドウ透過・OS 統合 | タイトルバーを消した不透明な unframed ウィンドウで独自 UI を構成したい。 | OS レベルのウィンドウ背景透過やトレイ常駐などが必要 → Electron / Tauri。 |
7. まとめ
最後に、本記事で解説した Isolated Web Apps(IWA)の要点を振り返ります。