Chrome に追加

仕組み

セキュリティを評価する方に向けた技術概要です。ここに書かれていることはすべてソースコードで確認できます。都合の悪い点も省かずに明記しています。

アーキテクチャ

バックエンドはありません。本拡張機能は Manifest V3 の Chrome 拡張機能で、TOTP/HOTP コードを OTPAuth を使ってローカルで生成します。サーバーは運用しておらず、アカウントも保持せず、利用者のデータが当方に届く経路そのものがありません。

サーバーがない以上、サーバー側の鍵素材も、鍵の預託も、当方が管理する復旧手段も存在しません。パスワードを忘れ、リカバリーコードも失った場合、当方にできることはありません。これは設計上の選択です。

暗号方式

パスワード保護は任意で、既定では無効です。有効にした場合:

暗号
AES-256-GCM、レコードごと・書き換えごとにランダムな 96 ビット IV
鍵導出
PBKDF2-HMAC-SHA256、600,000 回反復、128 ビットのランダムソルト(OWASP の指針に準拠)
マスターキー
256 ビット。crypto.getRandomValues から一度だけ生成し、パスワードはそれを包むだけです
フィンガープリント
HKDF で導出したサブキーによる HMAC-SHA256。端末間でレコードを統合するために使用します
解錠済みの鍵の保管先
chrome.storage.session — メモリ上のみ。ディスクには一切書き込まず、ブラウザーを閉じると消去され、コンテンツスクリプトからは到達できません
自動ロック
開くたび/操作しないまま 5・15・60 分経過後/ブラウザーを閉じるまで
実装
WebCrypto のみを使用。プリミティブの自作はなく、暗号ライブラリーの同梱もありません。

鍵を二段構えにしている理由

データをパスワード由来の鍵で直接暗号化することはありません。レコードを暗号化するのはランダムなマスターキーで、PBKDF2 の出力はそのマスターキーを包むだけです。ここから二つの結果が生まれ、どちらも重要です:

  • パスワードを変更しても書き換えるのは 32 バイトだけで、全レコード・全バックアップ・全エクスポートを暗号化し直す必要がありません。ストレージがデータを失うのは、まさにその種の一括書き換えの最中です。
  • 160 ビットのリカバリーコードが同じマスターキーを独立に包めるため、パスワードを忘れても復旧できます。コードは一度だけ表示し、確認のために入力し直す必要があり、使用のたびに更新されます。利用者が確認するまでディスクには何も書き込みません。

暗号化されるもの、されないもの

パスワード保護を有効にすると、識別子とフィンガープリントを除くアカウントレコード全体が暗号化されます。サービス名も対象なので、プロファイルを盗まれても、その人がどのサービスにアカウントを持っているかは分かりません。有効化の際は、レコードを暗号化し、復号して突き合わせることで検証したうえで、初めて平文のコピーをすべて削除します。ブラウザーのローカルストレージ、同期、そして 7 世代のローテーションバックアップのすべてが対象です。

評価に関わる点なので、率直に記します:

  • パスワード保護が無効の場合、アカウントは暗号化されずに保存され、Chrome 同期が有効ならば利用者自身の Google アカウントを通じて複製されます。当方に届くことは決してありませんが、Google は保持します。同期は無効にでき、その際すでに同期済みの内容も削除されます。
  • サイトごとの利用履歴(どのアカウントをどのドメインで使ったか)はローカルに保存され、収集されている間は保管庫の保護対象外です。パスワード保護を有効にすると削除されます。単独で無効にすることもできます。

脅威モデル

防げるもの

  • ブラウザーのプロファイルディレクトリーの盗難・複製
  • ブラウザーのデータをまとめて持ち出すインフォスティーラー
  • 放置された端末に物理的に触れられること
  • Chrome 同期を通じてアカウント情報が Google に渡ること
  • バックアップファイルの紛失・盗難(パスワード保護付きエクスポート)

防げないもの

  • 保管庫が解錠されている間、利用者権限で動作するマルウェア
  • 入力中のパスワードを取得するキーロガー
  • 侵害されたブラウザーや OS
  • コードの対象である第三者サービス自体の侵害

要するに、これが守るのは保存状態のデータです。使用中の時点ですでに攻撃者の制御下にある端末を守ることはできませんし、できるとも言いません。

権限

マニフェストが宣言する権限はちょうど二つ、ホスト権限はゼロです:

storage
アカウントと設定はローカルストレージ、解錠済みの鍵はセッションストレージ、そして任意の同期用コピー
activeTab
該当アカウントを強調表示するためにアクティブタブのホスト名を読み取り、画面から QR コードを読み取る際にそのタブをキャプチャーします

コンテンツスクリプトは一切なくtabscookieswebRequest の各権限もなく、web_accessible_resources もありません。本拡張機能はどのページの内容も読み取ることも書き換えることもできません。

カメラ

カメラによる QR 読み取りは、ポップアップではなくタブで開いた拡張機能ページ上で動作します。ポップアップはフォーカスを失うと破棄され、権限ダイアログはまさにそれを引き起こすからです。これにはマニフェスト上の権限は不要です。ブラウザー標準のカメラ許可ダイアログを使い、拡張機能のオリジン単位で付与され、サイトの設定から取り消せます。スキャナーを開くまで要求されることはありません。映像はローカルでデコードされ、送信も保存も一切されません。

外部への通信

通常利用では自動的な通信は一切行いません。また Manifest V3 はリモートコードを禁じています。発生しうる通信は以下がすべてです:

ホストタイミング利用者データを含むか
worldtimeapi.org
timeapi.io
時刻のずれの確認。キャッシュあり。失敗しても黙って続行します。時刻がずれると TOTP は成立しません。含まない
authenticator.shインストール時のようこそページ、アンインストールや評価時のフィードバックページ。含まない(通常の Web サーバーログのみ)

フォント・スクリプト・スタイル・画像のいずれも第三者ホストからは読み込みません。画面表示に必要なものはすべてパッケージ内に同梱されています。

公開物を検証する

以上のどれも、こちらを信用して受け入れる必要はありません。本拡張機能はオープンソースで、各リリースは再現可能です:

  1. Chrome ウェブストアで公開されている .crx をダウンロードして展開する
  2. 対応する git タグをチェックアウトする
  3. Node 20 LTS で npm ci && npm run build を実行する
  4. 生成された dist/ と展開したパッケージを比較する

生成された dist/ 内の全ファイルの SHA-256 を、各 GitHub リリースに SHA256SUMS-v<version>.txt として sha256sum 形式で添付しています。したがって手順 4 は sha256sum -c を一度実行するだけで済みます。差分はアーカイブ内のファイル順序と、Node のパッチバージョン差による minify 出力の空白に限られるはずです。

実行時の依存は意図的に少なく、OTPAuth・React・jsQR・Zustand・Framer Motion・Lucide のみで、コミット済みのロックファイルで固定しています。CycloneDX 形式の SBOM はご要望に応じて提供します。

ソースコードを見る

当方にないもの

当方は小規模な独立ベンダーです。次のいずれかが貴組織の必須要件であれば、評価を三週間進めた後よりも今知っていただくほうが良いはずです:

  • SOC 2、ISO 27001 などの認証は取得していません
  • 第三者によるペネトレーションテストの実施実績はありません
  • 有償のバグバウンティ制度はありません

その代わりに提供できるのは、ソースコードの完全な公開、検証可能なビルド、最小限の権限、そしてそもそも貴組織のデータを保持しうるサーバー基盤が存在しないという事実です。

連絡先

脆弱性の報告およびセキュリティに関するお問い合わせは security@authenticator.sh まで。開示ポリシー、対象範囲、応答時間、セーフハーバー条項はセキュリティポリシーのページに、何が保存され何が端末の外に出るのかはプライバシーポリシーに記載しています。

書面のベンダー評価アンケートへの回答や、セキュリティチームとのコードの読み合わせにも喜んで応じます。

セキュリティ以外の件(インストール、アカウントの復旧、導入に伴う請求関連など)はサポートをご覧ください。脆弱性の報告をそちらに送るのはお控えください。あの窓口は機密として扱っていません。