ブラウザ拡張の権限でどこまで悪いことをできるのか？とその対策【デモあり】

[2018/07/07 追記]
本記事ではChrome拡張について説明していますが、Firefox[^web-extensions]やEdgeの拡張機能もほぼ同じ仕組みで動いています。

[^web-extensions]: [Firefox 57(Firefox Quantum)からWebExtensionsが標準となりました](https://developer.mozilla.org/ja/Firefox/Releases/57#Changes_for_add-on_and_Mozilla_developers)

[2023/11/06 追記]
[#参考](#参考) ページを追加しました。

----

Chrome拡張。便利な機能を簡単に追加できるので使っている人も多いと思います。
ただ、インストール時の権限の注意書きが分かり難いので無条件に承認（追加）していることもあるのではないかと思われます。
![Chrome拡張インストール時の確認画面のキャプチャ](https://qiita-image-store.s3.amazonaws.com/0/92375/f23c2a76-2e72-1155-ac5a-bce37d6ee253.png "Chrome拡張インストール時の確認")
そこで、本記事ではChrome拡張の権限の種類・確認方法の他、拡張がどこまで（悪いことを）できるのかとその対策を3段階の権限（危険性）レベルごとに紹介していきたいと思います。


# 便利だが危険性もあるChrome拡張
Chrome拡張をインストールすると、Webページを読むというブラウザ本来の機能だけでなく様々なことができるようになります。
例えば、Webメールの新着通知や記事などの次ページ読み込み、ページのレイアウト変更などが有名どころかと思います。

しかし、いろいろできるということはChrome拡張が勝手にいろいろ（悪いこと）をすることも可能ということです。

ちょうど先月（2016年11月）、悪意をもったChrome拡張により被害を受けそうになったという記事 [HTTP Headers という 5万人が使っている Chrome 拡張のマルウェア疑惑。セッション盗まれて BTC も盗まれそうになった話。 - clock-up-blog](http://blog.clock-up.jp/entry/2016/11/03/http-headers-malware "http://blog.clock-up.jp/entry/2016/11/03/http-headers-malware") が投稿されました。Twitterなどでバズったので読まれた方も多いのではないかと思います。


# Chrome拡張の権限
Chrome拡張の権限について以下の順番で紹介してきます。

1. 権限の種類
1. 確認方法
1. （開発者向け）権限の要求方法
1. 3段階の権限レベルごとの危険性と対策
    * 権限なし
    * 閲覧履歴の読み取り
    * 全ページのデータの読み取りと変更

## 権限の種類
よく使われている権限には以下のようなものがあります。

* ページ情報（本記事ではここにフォーカスします）
    * 閲覧履歴
    * ページ上の全データ
* クリップボード
* 通知
* ブックマーク
* 画面キャプチャ
* etc...

参考ドキュメント
[Declare Permissions - Google Chrome](https://developer.chrome.com/extensions/declare_permissions "https://developer.chrome.com/extensions/declare_permissions")


## いつ確認される/できるか？
* インストール/更新時に確認ダイアログがChromeによって出される
* インストール/更新後は拡張機能ページ `chrome://extensions/` から「詳細」をクリック

### インストール時

![image](https://qiita-image-store.s3.amazonaws.com/0/92375/72d81bc0-e0bf-7096-8c9e-9c61fe6a0238.png)

### 更新時
使用権限の追加がある場合、Chromeメニューボタンが！マークになって確認できます。

1. 権限の追加を伴うChrome拡張の追加通知  
    ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/cc4fcd0b-5498-5dde-d05f-71f2f163b332.png)
2. 通知を選択 → 権限追加の確認  
    ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/6f04cfb4-ee26-73ad-bd96-6a4a323f2269.png)
    * 要求される追加権限を確認できます


### インストール後
拡張機能ページ `chrome://extensions/` から「詳細」をクリック

1. 拡張機能ページ `chrome://extensions/`  
    ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/8cf1d971-3d56-ad3e-ed05-54feb8f58b9d.png)
2. 「詳細」をクリックで権限・ストアリンクなどを確認可能  
    ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/d1a2776f-4a89-5d9a-82e1-2903a3f8d8a5.png)


## 権限の要求記述 （開発者向け）
`manifest.json` に keyを `permissions` 値を配列で書きます。
[サンプルmanifest.json](https://github.com/tksugimoto/chrome-extension_test-extension-permission/blob/v0.2.0/manifest.json#L7-L11 "https://github.com/tksugimoto/chrome-extension_test-extension-permission/blob/v0.2.0/manifest.json#L7-L11")

```javascript:manifest.json(抜粋)
	"permissions": [
		"storage",
		"contextMenus",
		"activeTab"
	],
```

なお、 `manifest.json` に書いた権限すべてがインストール時にユーザーに通知/確認されるわけではありません。
上記の例だと、`storage`, `contextMenus`, `activeTab` のいずれもユーザーには通知されない種類の権限ため、インストール時の確認などでは「権限無し」となります。
ユーザーに通知されない権限は [Permission Warnings - Google Chrome](https://developer.chrome.com/extensions/permission_warnings#nowarning "https://developer.chrome.com/extensions/permission_warnings#nowarning") にまとまっています。

ちなみに、 `storage` はCドライブなどストレージへの無制限のアクセス権限ではなく、拡張が作り出したデータ（設定値など）の保存/読み取りです（参考ドキュメント：[chrome.storage - Google Chrome](https://developer.chrome.com/extensions/storage "https://developer.chrome.com/extensions/storage")）。


## 個人情報が収集され得る権限の種類
ページ情報にアクセス可能な権限を対象に、ユーザーへの影響度のレベルをつけてみました。
（完全に独自なので、ツッコミお待ちしております。）

### Level 1: 権限なし
これは一番要求される権限が少ないです。
しかし、権限無しでも特定の条件下でできることがあります。

#### 確認方法
* インストール時の確認ダイアログ  
    ![インストール時の確認ダイアログ](https://qiita-image-store.s3.amazonaws.com/0/92375/69fd392f-41d0-28a3-8b64-c14d43e3277f.png "インストール時の確認ダイアログ")
* 拡張機能ページ `chrome://extensions/` から「詳細」を確認した時の情報  
    ![拡張機能ページ chrome://extensions/ から「詳細」を確認](https://qiita-image-store.s3.amazonaws.com/0/92375/ec8805b2-f24a-4cc8-b22c-93cf6e8e0fe4.png "拡張機能ページ chrome://extensions/ から「詳細」を確認")

#### 何ができるか
* **ユーザーが特定の操作をした時にのみ**ページ情報取得
    * 右クリックメニューから拡張のメニュー選択
    * URLバー右の拡張アイコンクリック
    * 拡張に設定されているキーボードショートカット押下
* 取得可能なページ情報
    * ページタイトル
    * ページURL
    * ページソース全て
        * 読み取り
            * ログインフォームの入力文字
                * **パスワード含む**
                * Chromeによる自動入力の場合含む
            * （例：オンラインバンキング）口座番号・残高
            * など
        * 変更
            * リンク先の変更
            * （例：ECサイト）注文数の変更
            * など
        * 操作
            * 文字入力
            * クリック
            * 新規リクエスト（ `fetch`, `XMLHttpRequest` ）作成
            * など
    * Cookie (非httpOnly)

#### デモChrome拡張
* ソースコード→ [GitHub: tksugimoto/chrome-extension_test-extension-permission](https://github.com/tksugimoto/chrome-extension_test-extension-permission "https://github.com/tksugimoto/chrome-extension_test-extension-permission")
* Chrome ウェブストア → [Chrome拡張の権限テスト - Chrome ウェブストア](https://chrome.google.com/webstore/detail/chrome%E6%8B%A1%E5%BC%B5%E3%81%AE%E6%A8%A9%E9%99%90%E3%83%86%E3%82%B9%E3%83%88/jdmbpgfahplabcaknkbkbcfdhjdkdfgg?hl=ja&authuser=5 "https://chrome.google.com/webstore/detail/chrome拡張の権限テスト/jdmbpgfahplabcaknkbkbcfdhjdkdfgg?hl=ja&authuser=5")
* 拡張の機能
    * 右クリックメニューのメニュー選択などをした時に、閲覧中のページ内の入力欄の情報を集めて別タブに表示
        * 右クリックメニューから拡張のメニュー選択
            * メニュー「入力欄のテキストを収集するテスト」
        * URLバー右の拡張アイコンクリック
        * 拡張に設定されているキーボードショートカット押下
            * [デフォルトショートカットキーを設定している](https://github.com/tksugimoto/chrome-extension_test-extension-permission/blob/v0.2.0/manifest.json#L20-L23 "https://github.com/tksugimoto/chrome-extension_test-extension-permission/blob/v0.2.0/manifest.json#L20-L23") ので、既に他の拡張で使用されていなければ以下のショートカットでも動作します（Windowsで動作確認済み）
                * Mac: `Command+Shift+Y`
                * その他: `Ctrl+Shift+Y`
    * ページタイトル・ページURLも表示
* 例
    1. 適当なページで右クリック→「入力欄のテキストを収集するテスト」を選択  
        ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/daed7b6f-0ea1-12f5-72d0-b38411d73607.png)
    2. 新しいタブで入力欄の情報が一覧表示  
        ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/8db418f4-8f87-f557-c84e-1f643fe2c300.png)
        * ※ パスワードは3文字目以降を `*` にするようにしています。ブラウザのConsoleを開いてページ更新で全ての入力値がコンソールに表示されます。
        * ※ 表示されているパスワード（ `zx****...` ）はデモ用に適当に入力しました（念のため）。


#### 対策
* ログインフォームなど重要な情報が含まれているページでは拡張に関連する操作をしない
    * 右クリックメニューから拡張のメニュー選択
    * URLバー右の拡張アイコンクリック
    * 拡張に設定されているキーボードショートカット押下
* 気にしない
    * 攻撃者の立場からすると、ユーザーが何か操作したときにしか情報を収集できないのは非効率すぎるので、わざわざ「権限無し」で悪意ある拡張を配布することは少ないのではないかと思います
    * まずは、このタイプの拡張よりも Level 2以降の注意すべき権限を要求している拡張に気をつけるべきです


### Level 2: 閲覧履歴の読み取り
閲覧履歴の取得が可能です。
また、Level 1（権限なし）でできることも同様にできます。

#### 確認方法
* インストール時の確認ダイアログ  
    ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/c6e91aac-6bc0-f086-1fe7-d798384c3f0c.png)
* 拡張機能ページ `chrome://extensions/` から「詳細」を確認した時の情報  
    ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/1aa80130-566c-9d24-e9ec-432bed6dc92b.png)

#### 何ができるか
* ユーザーがどんなページを見ているか取得可能
    * ページURL
    * ページタイトル
    * ページfavicon
    * 閲覧開始時刻～終了時刻
    * タブ / ウィンドウの切り替え

#### デモ拡張
* ソースコード→ 公開しません（悪用防止の為）
* Chrome ウェブストア → 公開しません
* 拡張の機能
    * ユーザーがいつ、どのページを見た/閉じたかを記録します
    * ユーザーの（インストール以外の）操作は必要ありません
    * インストールしてしまったら自動で全ての閲覧ログを記録します
* 例  
    ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/74c530cf-3fc6-2eab-0b73-3c36765a970c.png)

#### 対策
##### ユーザー向け
* 本当に「閲覧権限の読み取り」が必要か検討
    * 不要な場合（怪しいことをしているか、「とりあえず `tabs` 権限を追加」している可能性があります）
        * 類似の拡張機能でより権限の少ないものがないか検索
        * 自作！
    * 必要な場合
        * レビューを確認（しないよりはまし程度）
            * 権限についての指摘がないかどうか
            * ※ 指摘がない場合でも安全であることを保証しているとは限りません
            * ※ ユーザー数が多いことも安全であることを保証しません
        * ソースコードが公開されていれば確認
            * 悪意ある攻撃者がわざわざソースコードを公開することは少ないのではないかと思います
                * ※ 公開したソースコードに手を加えたものをChrome ウェブストアで公開することも可能です
            * 確実なのは、ソースコードの安全を確認 → そのソースコード（ローカル）からChromeに拡張インストールすることです
                * 拡張の更新時に悪意あるコードが追加されるとされるということも回避できます

##### 開発者向け（疑われないために）
* `tabs` 権限の削除または `activeTab` 権限への変更を検討してみてください
    * `chrome.tabs` APIのすべてのメソッドは `tabs` 権限なしでも使えます
        * タブを開く, タブ関連のイベントリスナー登録など
        * ※ ユーザーの操作なしに `url`, `title`, `favIconUrl` にアクセスしたいときだけ `tabs` 権限が必要です
            * [chrome.tabs.query メソッド](https://developer.chrome.com/extensions/tabs#method-query "https://developer.chrome.com/extensions/tabs#method-query") で `url`, `title`, `favIconUrl` をキーにした検索時も必要
    * ページの `url`, `title`, `favIconUrl` を取得したい場合も、ユーザーの操作（右クリックメニュー、アイコンクリックなど）のときで良いなら `activeTab` 権限で十分です
    * 参考
        * [The activeTab permission - Google Chrome](https://developer.chrome.com/extensions/activeTab "https://developer.chrome.com/extensions/activeTab")
        * [chrome.tabs - Google Chrome](https://developer.chrome.com/extensions/tabs "https://developer.chrome.com/extensions/tabs")



### Level 3: 全ページ（ or 特定ページ）のデータの読み取りと変更
**<font color="red">やりたい放題</font>**できます。

全ページ対象の場合は、Level 2（閲覧履歴の読み取り）でできることも同様にできます。
特定ページ対象の場合は、そのページの閲覧状況のみ取得可能です。

#### 確認方法
* インストール時の確認ダイアログ  
    * 全ページ対象の場合  
        ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/f23c2a76-2e72-1155-ac5a-bce37d6ee253.png)
    * 特定ページ(GitHub)のみ対象の場合  
        ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/ddc113c6-e019-65d6-5136-ffcb59e6340d.png)

* 拡張機能ページ `chrome://extensions/` から「詳細」を確認した時の情報
    * 全ページ対象の場合  
        ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/74cf8fb4-88e3-180d-3a23-e7c7ac40c20f.png)
    * 特定ページ(GitHub)のみ対象の場合  
        ![image](https://qiita-image-store.s3.amazonaws.com/0/92375/dc9648c4-b7a3-d7b3-75e9-e0bd098e6233.png)


#### 何ができるか
* Level 1（権限なし）のユーザー操作時にできたことを、ユーザーの操作なしで実行
    * ページタイトルの取得/変更
    * ページURLの取得/変更
    * ページソース全て
        * 読み取り
            * ログインフォームの入力文字
                * **パスワード含む**
                * Chromeによる自動入力の場合含む
            * （例：オンラインバンキング）口座番号・残高
            * など
        * 変更
            * リンク先の変更
            * （例：ECサイト）注文数の変更
            * など
        * 操作
            * 文字入力
            * クリック
            * 新規リクエスト（ `fetch`, `XMLHttpRequest` ）作成
            * など
    * Cookie

つまり、**ページを勝手に開いて、Chromeのパスワード入力機能によるパスワードが入力されたらその値を外部へ送り、そのページ（タブ）は閉じておく**ということもできてしまうのです。
他にも、**SNSや掲示板を開いて適当な文章を勝手に投稿する**ことも可能です。

#### 対策
##### ユーザー向け
* 本当に全ページのデータへのアクセス権が必要か検討
    * 不要な場合（怪しいことをしているか、「とりあえず全ページへのアクセス権限を追加」している可能性があります）
        * 類似の拡張機能でより権限の少ないものがないか検索
        * 自作！
    * 必要な場合
        * レビューを確認（しないよりはまし程度）
            * 権限についての指摘がないかどうか
            * ※ 指摘がない場合でも安全であることを保証しているとは限りません
            * ※ ユーザー数が多いことも安全であることを保証しません
        * ソースコードが公開されていれば確認
            * 悪意ある攻撃者がわざわざソースコードを公開することは少ないのではないかと思います
                * ※ 公開したソースコードに手を加えたものをChrome ウェブストアで公開することも可能です
            * 確実なのは、ソースコードの安全を確認 → そのソースコード（ローカル）からChromeに拡張インストールすることです
                * 拡張の更新時に悪意あるコードが追加されるとされるということも回避できます

##### 開発者向け（疑われないために）
* 全ページへのアクセスする権限の削除または 一部のページへのアクセスする権限への変更を検討してみてください  
    とりあえず `<all_urls>` と書いている拡張があったりしないでしょうか？
    * 対象の権限
        * `<all_urls>`
        * `*://*/*`
        * `http://*/`
        * `https://*/`
    * ユーザーの操作時のみの動作で良いなら `activeTab` 権限を使うこともありだと思います。
    * 参考
        * [Match Patterns - Google Chrome](https://developer.chrome.com/extensions/match_patterns "https://developer.chrome.com/extensions/match_patterns")
        * [The activeTab permission - Google Chrome](https://developer.chrome.com/extensions/activeTab "https://developer.chrome.com/extensions/activeTab")

# おわりに
Chrome拡張は便利な反面、**やろうと思えばなんでもできてしまう**ということが分かっていただけたでしょうか？
この記事をきっかけに少しでもChrome拡張の「**権限**」を意識する人が増えたらうれしいです。


# 参考
より詳細を知りたい場合、以下の記事も参考になります。

- [Malicious Extensions - What They Are And How To Fight Them · Profero](https://profero.io/posts/browserextentions/)

----

この記事は [Tech-Circle Hands on Advent Calendar 2016 - Qiita](http://qiita.com/advent-calendar/2016/techcircle "http://qiita.com/advent-calendar/2016/techcircle") の3日目の記事でした。
昨日（2日目）は [Sheile](http://qiita.com/Sheile "http://qiita.com/Sheile") さんの [2年ぐらいJavaScriptから離れていた人に贈るES6対応表](http://qiita.com/Sheile/items/f1e5416e53a83157c38c "http://qiita.com/Sheile/items/f1e5416e53a83157c38c") でした。
明日（4日目）は [bbrfkr](http://qiita.com/bbrfkr "http://qiita.com/bbrfkr") さんの [ansibleのロールを直接実行できる、ansible-artというツールを作った。](http://qiita.com/bbrfkr/items/ecc6b03f754e5fe01225 "http://qiita.com/bbrfkr/items/ecc6b03f754e5fe01225") です。