記事公開日
ブラウザ拡張機能を作って業務環境をセキュアにする(基礎編)

こんにちは。DXソリューション営業本部の吾妻です。
4月になり、新年度が始まりました。
人事異動や転勤などで人が動くシーズンであることから、セキュリティインシデントのリスクが高まる時期だといえます。
そこで、Power Platformに関する記事ばかり書いている普段とは趣向を変えて、業務環境のセキュリティを題材にお話ししたいと思います。
この記事を書いた動機
フィッシング詐欺とブラウザ拡張機能
最近は、業務システムとして、オンプレミス型ではなくクラウド型を採用することが多くなっています。すると、システムの利用者が業務システムとそれ以外のWebサイトに同一のブラウザからアクセスすることから、フィッシング詐欺によって不正アクセスされるリスクがあります。特に、始業直後から退勤するまで一日を通して利用するような、スケジューラーやメーラー(Outlookなど)、オンラインストレージ(Google Driveなど)のサービスは、単発で利用するサービスよりもブラウザセッションがサインインされた状態のまま維持されやすく、悪質なリンクをクリックしてしまった際に被害を受ける確率が高くなると考えられます。
フィッシング詐欺において、攻撃者は、利用者の心理的な隙、注意散漫な状況、ストレス(負荷)がかかった状態、情報過多といった、注意力が低下したタイミングを狙って攻撃することを狙っています。
例えば、代表的な攻撃手法の1つであるホモグラフ攻撃の場合、「rn(アールエヌ)」と「m(エム)」や、「o(オー)」と「ο(ギリシャ文字のオミクロン)」のように、フォントとして正規ドメインと見た目が紛らわしい偽のドメインを用意して、利用者の誤認を誘う手法であり、利用者の注意力に頼って防御することは困難だといえます。
また、利用者が日常的に繰り返している業務には慣れが生じることから、攻撃者に誘導されるままに行動してしまいがちです。
このため、このような攻撃による被害を防ぐために、ユーザーの注意力任せにするのではなく、ソフトウェアを用いて解決する方法が考えられます。
そこで、今回の記事では、この「防御するためのソフトウェア」を題材にして、ブラウザ拡張機能を実装する際に必要となる最低限の手順をご紹介したいと思います。
また、実装した拡張機能を配布する際に、一般的な「Chrome ウェブストア」を利用するのではなく、あえてMicrosoftのストアを利用する場合の流れについても検証してみたので、併せてご紹介したいと思います。
ブラウザ拡張機能
ブラウザ拡張機能とは
ブラウザ拡張機能とは、Webブラウザに機能を追加したり、カスタマイズしたりするためのプログラムのことです。「プログラム」といっても、HTMLやJavaScript、CSSといったWeb開発の基礎知識があれば、簡単に作ることができます。Webブラウザ自体だけでなく、ブラウザで表示しているWebページの内容を書き換えたり、コンテンツを追加することもできます。
第三者が作成した拡張機能をストアからダウンロードして利用しようとする場合には、提供元が信頼できる組織や開発者か確認したり、サイトの閲覧履歴や個人情報を収集されていないか検証したりする必要があります。自分自身で実装した拡張機能をインストールして利用することで、それらのセキュリティリスクを回避して、安全に、かつ、より快適にインターネットにアクセスすることができるようになります(当然、実装した箇所にも、実装に失敗している箇所にも、自分自身で責任を持つ必要がありますが)。
今回は、「基礎編」ということで、シンプルなブラウザ拡張機能を、3つのファイルのみで実装してみようと思います。フレームワークやNPMを利用して、より高度な拡張機能を実装する場合の手順については、別途記事をまとめたいと思います。
以前は、ブラウザごとに拡張機能の実装方法が異なっていたので、ブラウザごとに独立して拡張機能を開発する必要がありましたが、最近では、Mozilla FirefoxやMicrosoft Edgeの拡張機能も、Chromium系ブラウザの「extension API」との互換性が確保されているので、1つ拡張機能を開発してしまえば、軽微な調整をするだけでクロスブラウザ対応させることができます。
ブラウザ拡張機能で実現できる機能
ブラウザ拡張機能を実装することで実現できるシナリオの例を、フィッシング対策のものも、そうでないものも含めて、いくつかご紹介します。
①事前にホワイトリストに定義されている正規表現パターンに合致したURLにだけ、画面遷移できるようにする
②ホモグラフ攻撃を防ぐために、ハイパーリンクをクリッカブルでなくすことによって、条件反射的にリンクをクリックして偽サイトにアクセスしてしまうことを防ぐ
③Webメールで、アドレス帳に登録されているメールアドレス以外から届いたメールを非表示にする
④Web会議やデモの際に、画面に個人情報や機微情報(APIキーやクレデンシャル)が移りこまないように、固有名詞やGUIDなどを検索してマスクしたり、ぼかしたりする
⑤現在アクセスしているWebサイトが、本番環境なのか、開発環境なのか見間違えないように、ヘッダーを挿入して環境名を表示する
⑥記念日にだけ、Webページに紙吹雪を降らせる
他にも、一般的なWeb開発で実装できる機能であれば、たいてい拡張機能で実装することができます。
上の②や④の機能を持った「フィッシング詐欺を防ぐための拡張機能」のイメージを載せておきます。
実装上のポイントとしては、②ハイパーリンクの置き換えについては、ホワイトリンクに指定されていないURLへと遷移させるハイパーリンクを列挙したうえで、ハイパーリンクのラベル(<a>から</a>までの間に挟まれている文字列)と、リンク先URL(href属性)が異なっている場合に、<a>タグをcontentEditable属性付きの<div>タグに置き換えることによって、不審なハイパーリンクをクリックして遷移してしまうことを防ぐようなUIを実現しています。
また、④固有名詞のぼかしについては、正規表現で指定されたパターンに合致する文字列に対して、CSSでぼかし処理を行うことによって、氏名や会社名、APIキーといった、第三者に見せたくない情報を隠すUIを実現しています。
ブラウザ拡張機能の実装手順
続いて、ブラウザ拡張機能を作成する大まかな手順をご説明したいと思います。
まずは、プロジェクト用のフォルダを1つ作成して、そこにファイルを追加していきます。
拡張機能を実装する際に最低限必要なファイルとして、以下の2つがあります。
①マニフェストファイル(manifest.json)
…拡張機能のメタデータや権限について記述する定義ファイル
②コンテンツスクリプト(***.js)
…マニフェストファイルで定義したURLパターンに一致するWebページで静的に読み込まれるJavaScriptまたはCSSファイル
また、拡張機能の目的ごとに、必要に応じて以下のようなファイル・フォルダを追加することもできます。
③オプションページ(options.htmlなど)
…拡張機能の設定画面を定義するHTMLファイルや、そこで入力した設定値をstorage.sync APIで保存するためのJavaScriptファイル
④ライブラリ
…Bootstrapなどの外観を整えたり、ロジックを実装したりするためのライブラリ(静的に呼び出せるものに限られる)
それでは、ファイルの内容を具体的に見ていきたいと思います。
①マニフェストファイル(manifest.json)
拡張機能の名前やバージョン、説明、必要とするアクセス権限といったメタ情報を定義するためのファイルです。赤字で記載している箇所は注釈なので、実際のファイルには記載しません。
{ "name": "My extension", "version": "0.0.1", "manifest_version": 3, ←現在のマニフェストファイルのバージョンは「3」 "description": "フィッシング対策のための拡張機能", "permissions": ["tabs", "scripting", "storage"], ←利用したいAPIの権限を指定する "host_permissions": [ ←拡張機能がAPIにアクセスできるホストを指定する "https://*.google.com/*", "https://*.office.com/*", "https://www.qes.co.jp/*" ], "content_scripts": [ { "js": ["sample.js"], ←コンテンツスクリプトのファイル名を指定する "matches": [ ←コンテンツスクリプトを実行するホストを指定する "https://*.google.com/*", "https://*.office.com/*", "https://www.qes.co.jp/*" ], "run_at": "document_end", ←スクリプトをページに挿入するタイミングを指定する "all_frames": true } ], "options_page": "options.html" ←オプションページのファイル名を指定する } |
②コンテンツスクリプト(***.js)
コンテンツスクリプトに関しては、拡張機能に実装したい機能によって内容が異なるので詳細は省きます。
一般的には、以下のような処理を定義する必要があります。
項目 | 内容 |
初期化処理 | ページ(画像なども含めて)の読み込みが完了した時点で実行したい処理を、ページのloadイベントに定義する(DOMContentLoadedイベントで十分な場合もある) |
初期化処理(遅延実行) | ページの読み込みが完了してから一定期間経過してから実行したい処理を、タイマー(setTimeoutメソッド)を用いて定義する |
設定情報の読み書き | chrome.storage APIを用いて、ブラウザに保存されているユーザーデータを取得したり、保存したりする |
ホワイト/ブラックリスト | 利用者が現在表示しているページが拡張機能を実行すべき対象か否かを判定する |
MutationObserver | DOM(HTMLを解析した結果のツリー構造)に加えられた変更を監視する |
DOMの書き換え | コンテンツを追加したり、スクリプトを実行することによって、利用者が閲覧するWebページの内容を変更する |
実装上のポイントとしては、DOMの書き換え処理(拡張機能のメイン処理)を起動させるトリガーとして、MutationObserverによる監視処理を利用することが挙げられます。
最近のモダンなWebアプリケーションの場合、ページ全体の読み込みとは別に、任意のタイミングでコンポーネントごとにDOMが書き換わることがあります。そのため、ページ全体のloadイベントを拾うだけではなく、MutationObserverを利用してDOMの更新を検出する必要があります。MutationObserverで監視する際には、拡張機能での更新処理によるDOMの更新を検出してしまうことを防ぐために、必要に応じて、監視を中断したり再開したりする必要があります。
もちろん、拡張機能を実行させる対象が静的サイトの場合など、通常のloadイベント、DOMContentLoadedイベントで十分トリガーとしての役割を果たせる場合もあるので、要件に応じて選択する必要があります。
③オプションページ
プロジェクトフォルダの配下に、「lib」(libraryのlib)フォルダを作成して、その中に(後述する)Bootstrap5のJavaScript・CSSファイルも用意している前提で、オプションページのUI・JavaScriptを定義してみた例を示します。
拡張機能の設定画面と、コンテンツスクリプト、ポップアップ画面から共通の設定値を読み込み/書き込みするサンプルがあまり見つからなかったので、以下に載せておきます。ここでは、コンテンツスクリプトから設定値を読み出せるように、オプションページのスクリプトでも、chrome.storage APIを用いて、ブラウザにユーザーデータを保存するようにしています。
<html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Settings</title> <link href="lib/bootstrap-5.3.0/bootstrap.min.css" rel="stylesheet"> </head> <body class="vw-100 vh-100"> <div class="alert alert-primary" role="alert" id="alert-saved" style="display:none;"> 保存しました! </div> <div class="container-fluid"> <div class="row"> <div class="col">ホワイトリスト設定:</div> </div> <div class="row"> <div class="col"><textarea class="form-control w-100 h-100" id="whitelist_urls" type="text" rows="20">https://github.com https://learn.microsoft.com https://nam.safelink.emails.azure.net https://teams.microsoft.com/ https://office.com</textarea></div> </div> </div> <script src="lib/bootstrap-5.3.0/bootstrap.bundle.min.js"></script> <script> const WHITELIST_URLS = `https://github.com https://learn.microsoft.com https://nam.safelink.emails.azure.net https://teams.microsoft.com/ https://office.com`; const sleep = (msec) => new Promise((resolve) => setTimeout(resolve, msec)); const init = _ => { chrome.storage.sync.get(["whitelist_urls"]).then((result) => { if(result.whitelist_urls){ document.getElementById("whitelist_urls").value = result.whitelist_urls; } else { chrome.storage.sync.set({ "whitelist_urls": WHITELIST_URLS }).then(() => { document.getElementById("whitelist_urls").value = WHITELIST_URLS; }); } }); }; const save = _ => { chrome.storage.sync.set({ "whitelist_urls": document.getElementById("whitelist_urls").value }).then(async _ => { // 保存完了したら1秒間だけ通知を表示する const alertElement = document.getElementById('alert-saved'); alertElement.style.display = 'block'; await sleep(1000); alertElement.style.display = 'none'; }); }; document.addEventListener("DOMContentLoaded", _ => { init(); document.getElementById("whitelist_urls").addEventListener("change", save, false); }); </script> </body> </html> |
④ライブラリ
Azure Static Web Apps(私の推しサービス)やAmazon S3などで、静的サイトを公開するときと同じように、ライブラリのJavaScript・CSSファイルを組み込んで利用することができます。
例えば、拡張機能のUIを整えるためにBootstrap5を組み込みたい場合には、公式サイトから「コンパイルされたCSSとJS」(bootstrap.bundle.min.jsとbootstrap.min.css)をローカルにダウンロードしておき、プロジェクトフォルダの中の「lib」フォルダに格納します。
ファイル・フォルダ構成の例
これらのファイルを格納すると、プロジェクトフォルダの中身は以下のようになります。
プロジェクトフォルダ
│ manifest.json │ options.html │ sample.js └─libフォルダ └─bootstrap-5.3.0フォルダ bootstrap.bundle.min.js bootstrap.bundle.min.js.map bootstrap.min.css bootstrap.min.css.map |
ブラウザ拡張機能の実行/配布方法
このようにブラウザ拡張機能を実装したら、次は実行・配布したくなるかと思います。他のユーザーに対して拡張機能を配布する方法として、以下のようなものがあります。
①サイドローディング
…ブラウザの開発者モードを有効化したうえで、manifest.jsonなどのファイルを含むフォルダを選択して、拡張機能として読み込む
…ソースコード一式をフォルダごと配布して、受け取ったユーザーは開発者と同じ手順でブラウザに読み込んで実行する
②ストアでの配布
…MicrosoftやChromeのストアに、zip圧縮したファイルをアップロードして、審査を受けたうえで拡張機能を公開する
…利用者は、ストアから拡張機能をインストールして実行する
③レジストリまたはJSONファイル、更新マニフェスト(XML)での配布
…管理者が、組織内のすべてのユーザーに対して拡張機能をインストールさせたい場合に用いる方法
④クライアントアプリケーションへのバンドル
…クライアントアプリケーションをインストールすると、拡張機能もインストールされるようにする方法
ローカルPCでデバッグする間は、①サイドローディングで実行することで、ストアでの審査を待つことなく、拡張機能の動作確認を進めることができますが、サイドローディングはあくまでも開発者向けの機能なので、以下のような警告が表示されることがあり、永続的に使い続けることは推奨されません。
開発が終わり、大勢のユーザーに展開するタイミングになったら、②ストアでの配布を行います。
ブラウザ拡張機能のストアとしては、Googleが提供している「Chrome ウェブストア」が有名ですが、Microsoft Edge向けに拡張機能を公開するのであれば、Microsoftのストアを利用することもできます。
審査を受ける必要があることはChrome ウェブストアと同じですが、開発者登録の料金5ドルが掛からないのがメリットです。審査のための説明文や画像リソースなども、Chrome ウェブストアで登録するときとほぼ同じものを、同じような流れで「Microsoftパートナーセンター」から提出すれば大丈夫です。
Microsoftによる審査が完了すると、自動的に「Microsoft Edgeアドオン」に公開されます。機能上、動作上の問題が特になければ、2週間もあれば審査が終了するようです。
まとめ
今回は、フィッシング詐欺対策を題材として、ブラウザ拡張機能を作る際に必要となる知識について簡単にご紹介しました。
本文では触れていませんが、Microsoft 365を利用していて、特にPower AppsやPower Automateでアプリ・フローの内製開発を行っている場合には、ブラウザ上で業務を行う時間が長くなるかと思いますので、セキュリティリスクに目を向けて、よりセキュアに業務を進めるためにはどうすればいいか考えておく必要があるかと思います。
QES では Power Platform の開発支援、QAサポート、開発者教育、ガバナンス整備など、組織で Power Platform を活用するためのサポートを包括的にご提供しています。Power Platform 活用についてご興味がある/利用中だが課題を感じていらっしゃるお客様はまずはお気軽にお問い合わせください。
このブログで参照されている、Microsoft、Windows、その他のマイクロソフト製品およびサービスは、米国およびその他の国におけるマイクロソフトの商標または登録商標です。
Mozilla、Firefox、および Firefox のロゴは、米国およびその他の国におけるMozilla Foundationの商標です。
Google、Google Workspace、Google Chrome、Chromium、Gmailなどのサービス名は、Google LLCの商標です。
Vivaldiは、Vivaldi Technologiesの商標です。
Safariは、Apple Inc.の商標です。