Electron公式ドキュメントから推奨セキュリティツールが消えた。今、何でチェックすればいいのか
Electronのセキュリティチェックリスト(20項目のあれ)を見たことがある人は多いと思います。
あのページ、少し前まで Electronegativity という静的解析ツールを案内していました。それが削除されました。
つまり今、公式ドキュメントは「この20項目を手で確認して、リリースのたびに維持してください」とだけ言っていて、自動化の手段を一つも案内していない状態です。
この記事では、何が消えたのか、なぜ消えたのか、今なら代わりに何が使えるのか、そしてツールなしでも最低限どこを見るべきかを、コード付きでまとめます。
この記事はZennに書いた記事をベースに、その後の公式ドキュメントの変更を踏まえて再構成したものです。
なぜ消えたのか
Electronegativity は Doyensec が2019年に公開したOSSで、ASTとDOMを解析して Electron 固有の設定ミスやアンチパターンを検出するツールでした。まさにチェックリストを自動化するためのもので、長らく公式が案内する唯一の選択肢でした。
ただ、機能面の更新は2022年ごろで止まっています。リポジトリにも積極的なメンテナンスをしていない旨が明記されていて、新しいElectronではうまく動かないケースがあります。
後継として有料版の ElectroNG(2022年登場、$688/年)がありましたが、こちらも現在は販売が終了しています。購入ページには「もう販売していない。ソースコードとインフラの買い手に関心があれば連絡してほしい」という趣旨が書かれています。
要するに、Electron専用の静的解析ツールという枠が、まるごと空きました。
今、代わりに何が使えるのか
「じゃあCIに何を入れればいいのか」を調べた結果を共有します。
CodeQL
汎用の静的解析基盤ですが、標準クエリに frameworks/electron 系のものが含まれていて、Electron固有のシンクをいくつかカバーしています。すでにCodeQLを回しているリポジトリなら追加コストはほぼゼロです。
ただし Electron のプロセスモデルに根ざした専用設計ではないので、チェックリストの項目を網羅的に見てくれるわけではありません。
Semgrep
チェックリストの項目を自分でルールとして書けます。これはかなり実用的で、「自分のアプリで実際に事故った箇所」だけを狭く書いて強制する、という使い方は特に相性がいいです。
注意点として、データフロー解析(taint)は無料版だと同じ関数のスコープ内に限られます。関数やファイルをまたいで値の流れを追う cross-file taint は有料のPro機能です。
汎用ツールで拾えないもの
ESLintプラグイン、npm audit、Snyk あたりは依存関係のCVEや一般的なJSのlintは拾ってくれます。ただ webPreferences の設定ミス、preloadの露出の仕方、IPCの検証漏れといった Electron固有のプロセスモデルの問題 は、まとめては見てくれません。
ツールなしでも最低限見るべき5箇所
ここからは実際のコードの話です。ツールを入れる入れないに関わらず、レビューで見るべきポイントとして使えます。
1. webPreferences
一番基本ですが、一番事故ります。
// 危険
new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
レンダラからNodeがそのまま触れる状態です。XSS一つで、ファイルシステムへの読み書きやコマンド実行に到達します。
// 修正
new BrowserWindow({
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
sandbox: true,
preload: path.join(__dirname, "preload.js"),
},
});
補足として、nodeIntegration: false だけでは実は不十分で、Nodeのプリミティブを本当に遮断するには contextIsolation: true の併用が必要、というのがElectron公式の立場です。この2つはセットで見てください。
なお contextIsolation は Electron 12以降、sandbox は 20以降がデフォルトで有効です。新しめのバージョンを使っていて明示的にオフにしていないなら、この項目自体はすでに安全側に倒れています。問題になるのは「わざわざオフにしている」箇所です。
2. ウィンドウごとの設定の食い違い
個人的にこれが一番見落とされやすいと思っています。
メインウィンドウはちゃんと安全な設定になっているのに、子ウィンドウの生成箇所だけ設定が抜けている、というパターンです。
// メインは安全
const mainWindow = new BrowserWindow({
webPreferences: { contextIsolation: true, nodeIntegration: false },
});
// でも子ウィンドウだけ雑になっている
ipcMain.handle("open-win", (event, arg) => {
const child = new BrowserWindow({
webPreferences: { nodeIntegration: true, contextIsolation: false },
});
});
メインだけ見て安心していると通り抜けます。プロジェクト内のウィンドウ生成箇所を全部並べて比較するのが確実です。
3. コマンド実行(特に権限昇格を伴うもの)
深刻度で言えばこれが最上位です。
// 危険:外部由来の値がテンプレート文字列でシェルに渡り、しかも権限昇格される
sudo.exec(`networksetup -setdnsservers Wi-Fi ${userInput}`, options);
userInput に細工をされれば、管理者権限で任意のコマンドが動きます。sudo-prompt のような権限昇格の仕組みと組み合わさっている場合、影響は端末全体に及びます。
// 修正:シェルを介さず、引数を配列で渡す
execFile("networksetup", ["-setdnsservers", "Wi-Fi", validatedInput], options);
execFile に引数配列で渡せば、値がコマンドとして再解釈されることはありません。ただし shell: true を付けるとシェルを経由してしまうので、そこは別途注意が必要です。
4. CSP
default-src 'self'; script-src 'self' 'unsafe-inline'
unsafe-inline や unsafe-eval が入っていると、XSSに対する防御として穴が空きます。ソースにワイルドカードの * を使っている場合も同様です。
チェックする側の実装上の注意点として、*.example.com のような部分的なワイルドカードを素朴な文字列マッチで * と誤検知しないことが重要です。CSPはディレクティブ(; 区切り)とソース(空白区切り)でトークン単位に分解して見る必要があります。
5. shell.openExternal
// 危険:スキームの検証がない
shell.openExternal(urlFromRenderer);
http/https 以外のスキーム(file: など)を渡されると、意図しないものが開かれます。
// 修正:スキームをホワイトリストで検証
const parsed = new URL(urlFromRenderer);
if (parsed.protocol === "https:" || parsed.protocol === "http:") {
shell.openExternal(urlFromRenderer);
}
同じ発想で、setWindowOpenHandler や will-navigate で外部への遷移を制御しているかも合わせて見ておくといいです。
自動化したい場合
ここまで手で見るポイントを書きましたが、毎回のPRで人間が思い出して確認するという運用は、正直あまり長続きしません。40ファイルのPRが金曜の夕方に来たら、webPreferences の1行は普通に見落とします。これは人間の注意力の問題ではなく、CIの問題だと思っています。
なので、狭くていいので機械に強制させるのが現実的です。前述のとおり Semgrep で「自分のアプリで実際に事故った項目」だけルール化するのは、コスパのいい第一歩だと思います。
宣伝になってしまうので手短にしますが、自分でも electron-audit という OSS を作っていて、上記のようなパターンを検出しています(AST解析のみでアプリは実行しない、SARIF出力、MIT)。誤検知でビルドが落ちるのが一番嫌われると思っているので、検出ごとに confidence(確実/推測)を深刻度とは別に持たせて、CIの終了コードは確実な critical/high のみで落とす設計にしています。
他にも同じ枠を埋めようとしているツールがあれば、単純に知りたいです。
まとめ
- 公式ドキュメントから Electronegativity の推奨が削除された(#48878)
- 理由は Electronegativity のメンテ停止。後継の有料版 ElectroNG も販売終了済み
- 今のところ「公式が案内する自動チェック手段」は存在しない
- 代替候補は CodeQL、Semgrep(無料版のtaintは単一関数スコープ)、新しめのOSS
- ツールを入れないなら、最低限 webPreferences・ウィンドウ間の設定の食い違い・コマンド実行・CSP・openExternal は見ておく
チェックリスト自体は難しくありません。難しいのは、それを毎回のリリースで維持し続けることのほうです。