私はメールプライバシー拡張機能 Mailshade の開発者です。Mailshade 1.1.0 は、複数の Web メールを対象にした当社の管理されたブロッカー比較で最高の結果を記録しました。固定した 102 ケースでは既知トラッカー 43 件と無害な対照 59 件をすべて通過し、Trocker 3.4.1 はトラッカー 14 件を見逃し、無害な対照 3 件に影響しました。Mailshade 側の試験対象は、リリースメタデータ以外が 1.1.0 と機能的に同等な 1.0.8 release candidate です。ただし、これは 2026 年 8 月 16 日に Chrome 148 / macOS 26.5.2 で行った二つの固定バージョンの比較であり、市場全体や将来の全トラッカーを評価した結果ではありません。この記事では、その結果を支えた変更の一つ、必要な Web メールのホスト権限だけを利用者が選ぶ設計に絞ります。
構成と日本語の推敲には生成 AI の支援を使いました。権限名、対象 origin、要求・取消しの順序、実行時登録の条件は、公開版 1.1.0 の固定ソース、パッケージ化された manifest、テストと実 UI に照合しています。コードは説明のため一部を短くしています。
インストール時の権限と「機能を有効にしたい」という意思を分ける
Chrome 拡張で Web メールを六つサポートしていても、利用者が実際に使うのは一つか二つかもしれません。全サイトへのアクセスを最初から要求すると、機能の対象より権限の対象が広くなります。
Mailshade 1.1.0 の Chromium MV3 manifest は、必須の host_permissions を空にし、Web メールの origin を optional_host_permissions に置いています。Web メール部分だけを抜粋すると次の形です。
{
"permissions": [
"storage",
"declarativeNetRequest",
"alarms",
"contextMenus",
"scripting",
"activeTab"
],
"host_permissions": [],
"optional_host_permissions": [
"https://mail.google.com/*",
"https://outlook.live.com/*",
"https://outlook.office.com/*",
"https://outlook.office365.com/*",
"https://mail.superhuman.com/*",
"https://mail.yahoo.com/*",
"https://mail.proton.me/*"
]
}
ここに <all_urls> はありません。1.1.0 では、以前の実行時補助に使っていた任意の webNavigation API 権限も削除しました。つまり「Web メールを扱う拡張だから、すべてのページとナビゲーション API を先に許可してもらう」という形にはしていません。
図1 — 固定した公開版 1.1.0 と決定論的なローカルデモデータで取得した日本語 UI。実メールボックスの利用結果ではありません。レポートの六つのイベント元は、権限側でも独立したクライアントとして扱われます。
「クライアント」と「origin」を明示的に対応させる
UI のチェック項目を直接 URL 文字列へ変換するのではなく、サポート対象を型付きの対応表にします。
| Web メール | 要求する origin |
|---|---|
| Gmail | https://mail.google.com/* |
| Outlook | https://outlook.live.com/* |
| Microsoft 365 |
https://outlook.office.com/* と https://outlook.office365.com/*
|
| Superhuman | https://mail.superhuman.com/* |
| Yahoo Mail | https://mail.yahoo.com/* |
| Proton Mail | https://mail.proton.me/* |
この表には二つの役割があります。
- 権限ダイアログへ渡す origin を作る。
- そのクライアント用コンテンツスクリプトを登録してよいか判定する。
同じ対応表を使えば、「許可したホスト」と「実際にコードを注入するホスト」が別々に増殖しません。Microsoft 365 のように一つのクライアントが複数 origin を持つ場合も、その境界を一か所で確認できます。
選択した origin を一回のユーザージェスチャーで要求する
オンボーディングでは複数の Web メールをまとめて選べます。実装は選択されたクライアントを重複排除し、対応する origin を集め、クリックに結び付いた一回の chrome.permissions.request へ渡します。
async function requestSelectedClients(clients: ClientId[]) {
const uniqueClients = [...new Set(clients)];
const origins = [
...new Set(uniqueClients.flatMap(hostPatternsForClient)),
];
if (origins.length === 0) return false;
return chrome.permissions.request({ origins });
}
ここで重要なのは、Web メール単位で opt-in であることと、ブラウザーの確認ダイアログを一件ずつ連続表示することは別だという点です。選択単位はクライアントですが、要求は一つのユーザージェスチャーの中でまとめます。許可されなければ、その選択を有効状態として保存しません。
設定フラグだけでは実行を許可しない
settings.clients.gmail = true は、利用者の意思を表す永続設定です。しかし、ホスト権限そのものではありません。Chrome Sync で設定が別端末へ届いても、プロファイルローカルなサイト権限は一緒に移動しないからです。
そこで実行条件を次の積にします。
実行可能 = 同期された有効化の意思 AND このプロファイルのホスト権限
コンテンツスクリプトの登録処理は、各クライアントについて次の順で確認します。
- 設定でそのクライアントが有効か。
-
chrome.permissions.contains({ origins })が真か。 - 両方を満たす場合だけ、対応するスクリプトを
document_startで登録する。
どちらかが欠けた登録は削除します。これにより、同期された true をローカル権限の証拠と誤認して、別端末の受信箱へコードを注入することを避けられます。
図2 — 設定の意思とローカルなホスト権限を別の状態として扱い、両方がそろったクライアントだけを実行対象にします。
有効化と無効化を一つの論理操作として扱う
設定画面のトグルには、保存と権限変更という二つの副作用があります。順序を誤ると「権限だけ残る」「設定だけ有効になる」という半端な状態ができます。
有効化では、クリックのタスク内で現在の権限スナップショットと権限要求を開始し、許可後に設定を保存します。保存が失敗した場合は、その操作で新しく得た origin だけをスナップショットまで戻します。以前から付与されていた権限を巻き添えで取り消してはいけません。
無効化では、まず設定を無効にして、開いているタブから監視処理を外せる状態を作り、その後で対象クライアントの origin を取り消します。権限変更中は他のトグル操作をロックし、遅いコールバックが後の選択を追い越さないようにします。
このトランザクションはデータベースの原子的更新ではありません。それでも「どこまで成功したか」と「何をロールバックしてよいか」を明示すれば、失敗時に権限を広げたままにする確率を下げられます。
webNavigation を削除しても、挙動を推測で埋めない
1.1.0 の manifest から webNavigation を外した事実は、「ナビゲーションという概念が不要になった」という意味ではありません。意味するのは、利用者にその API 権限を要求することを実行条件にしないということです。
対象クライアントの早期注入は、権限を確認した動的コンテンツスクリプト、document_start、必要なクライアントでの allFrames と matchOriginAsFallback によって構成します。タブ修復処理も、利用できない API を権限があるように扱わず、安全な範囲へ縮退させます。
権限削減は manifest の一行を消すだけでは終わりません。削除後の代替経路と失敗時の縮退を実ブラウザーで確認して初めて、機能と権限の境界が一致します。
最小権限をテストするときの観点
実装レビューでは、少なくとも次を別々に検証します。
- 新規インストール直後の
host_permissionsが空である。 -
<all_urls>とwebNavigationが要求権限に含まれない。 - Gmail だけを選ぶと Gmail の origin だけが要求される。
- Microsoft 365 では二つの必要 origin が一緒に扱われる。
- 権限拒否時に設定とスクリプト登録が有効にならない。
- 同期設定が
trueでも、ローカル権限がなければ登録しない。 - 一つのクライアントを無効にしても、別クライアントの権限を削除しない。
- 設定保存失敗時に、操作前から存在した権限を取り消さない。
- 権限の追加・削除と起動処理が競合しても、最後の状態へ収束する。
最小権限は、権限一覧を短く見せるためのコピーではありません。「どの利用者の操作が、どの origin に、どのコードを、いつ実行させるか」を同じ対応表と実行条件で結び付ける設計です。機能フラグとブラウザー権限を別物として扱うと、サポート対象を増やしても権限範囲を一括で広げずに済みます。

