Electron セキュリティ設定チートシート:webPreferences/CSP/許可リスト早見表

Electron でアプリケーションを開発する際、最も重要なのがセキュリティ設定です。一つの設定ミスが、XSS 攻撃やリモートコード実行の脆弱性につながる可能性があります。
本記事では、Electron のセキュリティ設定の中核となる webPreferences
、Content Security Policy(CSP)、そして許可リスト(allowlist)の設定方法を、すぐに使えるチートシート形式でまとめました。初心者の方でも迷わず安全な設定ができるよう、具体的なコード例と共に解説していきます。
セキュリティ設定早見表
まず、すぐに使える主要なセキュリティ設定を一覧でご紹介します。
webPreferences の推奨設定一覧
# | 設定項目 | 推奨値 | 重要度 | 効果 |
---|---|---|---|---|
1 | nodeIntegration | false | ★★★ | Renderer での Node.js API 使用を禁止 |
2 | contextIsolation | true | ★★★ | Preload と Renderer のコンテキストを分離 |
3 | sandbox | true | ★★☆ | Chromium サンドボックスを有効化 |
4 | webSecurity | true | ★★★ | 同一オリジンポリシーなどを有効化 |
5 | allowRunningInsecureContent | false | ★★☆ | HTTPS 内での HTTP コンテンツ実行を禁止 |
6 | enableRemoteModule | false | ★★★ | remote モジュールを無効化 |
7 | nodeIntegrationInWorker | false | ★★★ | Worker での Node.js API 使用を禁止 |
8 | nodeIntegrationInSubFrames | false | ★★★ | iframe での Node.js API 使用を禁止 |
9 | preload | パス指定 | - | 安全な API を公開する Preload スクリプト |
CSP ディレクティブ推奨設定一覧
# | ディレクティブ | 推奨値 | 用途 |
---|---|---|---|
1 | default-src | 'self' | すべてのリソースのデフォルト |
2 | script-src | 'self' | JavaScript の読み込み元制限 |
3 | style-src | 'self' 'unsafe-inline' | CSS の読み込み元制限 |
4 | img-src | 'self' data: https: | 画像の読み込み元制限 |
5 | connect-src | 'self' + API ドメイン | XHR/WebSocket の通信先制限 |
6 | object-src | 'none' | プラグイン実行を禁止 |
7 | base-uri | 'self' | base URL を制限 |
8 | form-action | 'none' または 'self' | フォーム送信先を制限 |
必須セキュリティチェックリスト
# | チェック項目 | 確認方法 |
---|---|---|
1 | nodeIntegration が false になっているか | BrowserWindow の webPreferences を確認 |
2 | contextIsolation が true になっているか | BrowserWindow の webPreferences を確認 |
3 | CSP が設定されているか | HTML の meta タグまたはヘッダーを確認 |
4 | Preload スクリプトで contextBridge を使用しているか | preload.ts/js ファイルを確認 |
5 | ナビゲーションガードが設定されているか | will-navigate イベントハンドラを確認 |
6 | 外部リンクをブラウザで開く設定になっているか | setWindowOpenHandler の実装を確認 |
7 | DevTools で require が undefined になっているか | コンソールで typeof require を実行 |
8 | CSP 違反エラーが出ていないか | DevTools のコンソールを確認 |
背景
Electron のセキュリティリスク
Electron は、Chromium と Node.js を統合したフレームワークです。この統合により、Web 技術でデスクトップアプリを開発できる反面、Web とネイティブ環境の両方のセキュリティリスクを抱えることになります。
mermaidflowchart TB
app["Electron アプリ"] --> renderer["Renderer プロセス<br/>(Chromium)"]
app --> main["Main プロセス<br/>(Node.js)"]
renderer -->|"デフォルト設定では<br/>危険"| node["Node.js API<br/>アクセス可能"]
renderer -->|"外部コンテンツ"| xss["XSS 攻撃リスク"]
node --> rce["リモートコード<br/>実行の危険"]
xss --> rce
style rce fill:#ff6b6b
style xss fill:#ffd93d
style node fill:#ffd93d
上の図が示すように、Renderer プロセスが Node.js API に直接アクセスできる状態では、XSS 攻撃を受けた際にシステム全体が危険にさらされます。
Electron アプリが抱える主なセキュリティリスクは以下の通りです:
- Node.js API への不正アクセス:Renderer プロセスから直接ファイルシステムやプロセスを操作できてしまう
- 外部コンテンツの読み込み:信頼できないリモートコンテンツを読み込むことで XSS 攻撃を受けるリスク
- プロトコル制限の欠如:
file://
やdata:
プロトコルの無制限な利用による情報漏洩
セキュリティ設定の 3 本柱
これらのリスクを軽減するため、Electron には以下の 3 つの主要なセキュリティメカニズムが用意されています。
# | メカニズム | 役割 | 設定場所 |
---|---|---|---|
1 | webPreferences | プロセス間の権限分離を制御 | BrowserWindow 生成時 |
2 | CSP | コンテンツ読み込み元を制限 | HTML の meta タグまたはヘッダー |
3 | 許可リスト | 特定のリソースへのアクセスを明示的に許可 | webRequest API や設定ファイル |
これら 3 つを適切に組み合わせることで、多層防御によるセキュアな Electron アプリを実現できます。
課題
セキュリティ設定の複雑さ
Electron のセキュリティ設定には、いくつかの課題があります。
設定項目が多岐にわたる
webPreferences
だけでも 20 以上の設定項目があり、それぞれが相互に影響し合います。どの設定をどのように組み合わせるべきか、初心者には判断が難しいでしょう。
デフォルト設定の危険性
古いバージョンの Electron では、デフォルトで nodeIntegration: true
になっていました。この設定では、Renderer プロセスから直接 Node.js API にアクセスできてしまうため、極めて危険です。
バージョンによる違い
Electron のバージョンアップに伴い、デフォルト設定や推奨設定が変更されることがあります。古いドキュメントを参照していると、現在のベストプラクティスから外れた設定をしてしまう可能性があります。
よくある設定ミス
実際の開発現場でよく見られる設定ミスを見ていきましょう。
mermaidflowchart LR
mistake1["nodeIntegration: true"] --> risk1["Node.js API に<br/>直接アクセス可能"]
mistake2["contextIsolation: false"] --> risk2["グローバル汚染<br/>リスク"]
mistake3["CSP なし"] --> risk3["外部スクリプト<br/>実行可能"]
mistake4["allowRunningInsecure<br/>Content: true"] --> risk4["HTTPS 強制なし"]
risk1 --> danger["セキュリティ<br/>脆弱性"]
risk2 --> danger
risk3 --> danger
risk4 --> danger
style danger fill:#ff6b6b
style mistake1 fill:#ffd93d
style mistake2 fill:#ffd93d
style mistake3 fill:#ffd93d
style mistake4 fill:#ffd93d
これらの設定ミスは、それぞれ単独でも危険ですが、組み合わさることで攻撃の入口を大きく広げてしまいます。
# | 設定ミス | 危険性 | 攻撃例 |
---|---|---|---|
1 | nodeIntegration: true | ★★★ | XSS から OS コマンド実行 |
2 | contextIsolation: false | ★★★ | プロトタイプ汚染攻撃 |
3 | CSP の未設定 | ★★☆ | 外部スクリプトインジェクション |
4 | webSecurity: false | ★★★ | CORS 制限のバイパス |
5 | allowRunningInsecureContent: true | ★☆☆ | 中間者攻撃 |
これらの課題を解決するには、推奨設定を体系的に理解し、チートシートとして手元に置いておくことが効果的です。
解決策
webPreferences の推奨設定
まず、webPreferences
の推奨設定を見ていきましょう。これは BrowserWindow を生成する際に指定します。
基本設定:セキュアな BrowserWindow の作成
BrowserWindow を作成する際の基本的なセキュリティ設定です。
typescriptimport { BrowserWindow } from 'electron';
typescript// セキュアな BrowserWindow の作成
const createSecureWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// 以下でセキュリティ設定を指定
},
});
return win;
};
次に、webPreferences
の中身を段階的に設定していきます。
Node.js 統合の無効化
Renderer プロセスから Node.js API への直接アクセスを無効化します。
typescriptwebPreferences: {
// Node.js 統合を無効化(最重要)
nodeIntegration: false,
// Worker スレッドでの Node.js 統合も無効化
nodeIntegrationInWorker: false,
// サブフレーム(iframe など)でも Node.js 統合を無効化
nodeIntegrationInSubFrames: false,
}
nodeIntegration
を false
に設定することで、Renderer プロセスから require()
や process
などの Node.js API を直接使えなくなります。これにより、XSS 攻撃を受けてもシステムへの直接アクセスを防げます。
コンテキスト分離の有効化
Renderer プロセスと Preload スクリプトのコンテキストを分離します。
typescriptwebPreferences: {
// コンテキスト分離を有効化(最重要)
contextIsolation: true,
// Preload スクリプトの指定
preload: path.join(__dirname, 'preload.js'),
}
contextIsolation: true
にすることで、Preload スクリプトと Renderer プロセスが異なる JavaScript コンテキストで実行されます。これにより、グローバルスコープの汚染やプロトタイプチェーンの改ざんを防げるのです。
Web セキュリティの有効化
同一オリジンポリシーなどの Web セキュリティ機能を有効化します。
typescriptwebPreferences: {
// Web セキュリティを有効化
webSecurity: true,
// HTTPS ページ内での HTTP コンテンツ実行を禁止
allowRunningInsecureContent: false,
}
webSecurity: false
にすると、CORS(Cross-Origin Resource Sharing)制限がバイパスされ、任意のドメインからリソースを取得できてしまいます。開発時の便宜のために無効化する例を見かけますが、本番環境では必ず true
に設定してください。
リモートモジュールの無効化
古い Electron の remote
モジュールを無効化します。
typescriptwebPreferences: {
// remote モジュールを無効化
enableRemoteModule: false,
}
Electron 14 以降では @electron/remote
として別パッケージになっていますが、古いバージョンを使用している場合は明示的に無効化しましょう。remote
モジュールは Renderer から Main プロセスのオブジェクトに直接アクセスできるため、セキュリティリスクが高いです。
サンドボックスの有効化
Chromium のサンドボックス機能を有効化します。
typescriptwebPreferences: {
// サンドボックスを有効化
sandbox: true,
}
sandbox: true
にすると、Renderer プロセスが Chromium のサンドボックス内で実行されます。これにより、Renderer プロセスが侵害されても、システムへの影響を最小限に抑えられます。
ただし、サンドボックスを有効化すると、Preload スクリプトでも Node.js API の一部が使用できなくなります。この場合、contextBridge
を使った安全な API 公開が必要です。
完全な webPreferences 設定例
これまでの設定をまとめた、完全なセキュア設定の例です。
typescriptimport { app, BrowserWindow } from 'electron';
import * as path from 'path';
typescriptfunction createSecureWindow() {
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
// Node.js 統合関連(すべて無効化)
nodeIntegration: false,
nodeIntegrationInWorker: false,
nodeIntegrationInSubFrames: false,
// コンテキスト分離(有効化)
contextIsolation: true,
// Web セキュリティ(有効化)
webSecurity: true,
allowRunningInsecureContent: false,
// リモートモジュール(無効化)
enableRemoteModule: false,
// サンドボックス(有効化)
sandbox: true,
// Preload スクリプト
preload: path.join(__dirname, 'preload.js'),
},
});
return mainWindow;
}
typescriptapp.whenReady().then(() => {
const win = createSecureWindow();
// セキュアなコンテンツの読み込み
win.loadFile('index.html');
});
この設定が、2024 年時点での Electron セキュリティのベストプラクティスとなります。
webPreferences 設定早見表
すべての主要な webPreferences
設定を一覧表にまとめました。
# | 設定項目 | 推奨値 | 説明 | 危険度(false 時) |
---|---|---|---|---|
1 | nodeIntegration | false | Renderer での Node.js API 使用を禁止 | ★★★ |
2 | nodeIntegrationInWorker | false | Worker での Node.js API 使用を禁止 | ★★★ |
3 | nodeIntegrationInSubFrames | false | iframe での Node.js API 使用を禁止 | ★★★ |
4 | contextIsolation | true | Preload と Renderer のコンテキストを分離 | ★★★ |
5 | webSecurity | true | 同一オリジンポリシーなどを有効化 | ★★★ |
6 | allowRunningInsecureContent | false | HTTPS 内での HTTP コンテンツ実行を禁止 | ★★☆ |
7 | enableRemoteModule | false | remote モジュールを無効化 | ★★★ |
8 | sandbox | true | Chromium サンドボックスを有効化 | ★★☆ |
9 | preload | パス指定 | 安全な API を公開する Preload スクリプト | - |
CSP(Content Security Policy)の設定
CSP は、読み込み可能なリソースの出元を制限するセキュリティ機構です。
CSP の基本概念
CSP を設定することで、以下のような攻撃を防ぐことができます:
- インラインスクリプトの実行防止
- 外部ドメインからのスクリプト読み込み制限
eval()
などの危険な関数の使用禁止
mermaidflowchart TB
page["HTML ページ"] --> csp["CSP ヘッダー"]
csp --> allow["許可リスト"]
csp --> block["ブロックリスト"]
allow -->|"self のみ"| script1["同一オリジン<br/>スクリプト OK"]
allow -->|"特定ドメイン"| script2["cdn.example.com<br/>OK"]
block -->|"それ以外"| script3["外部スクリプト<br/>ブロック"]
block -->|"inline"| script4["インラインスクリプト<br/>ブロック"]
style script3 fill:#ff6b6b
style script4 fill:#ff6b6b
style script1 fill:#95e1d3
style script2 fill:#95e1d3
上の図のように、CSP は許可するリソースを明示的に指定し、それ以外をすべてブロックする「ホワイトリスト方式」で動作します。
HTML での CSP 設定
HTML ファイルの <head>
内に <meta>
タグで CSP を設定できます。
html<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<!-- CSP の設定 -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>セキュアな Electron アプリ</title>
</head>
</html>
この設定では、同一オリジン('self'
)からのリソースのみを許可しています。
厳格な CSP 設定例
最も厳格な CSP 設定の例です。
html<meta
http-equiv="Content-Security-Policy"
content="
default-src 'none';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
base-uri 'self';
form-action 'none';
"
/>
各ディレクティブの意味は以下の通りです:
default-src 'none'
:デフォルトですべてのリソース読み込みを禁止script-src 'self'
:スクリプトは同一オリジンのみ許可style-src 'self' 'unsafe-inline'
:CSS は同一オリジンとインライン CSS を許可img-src 'self' data:
:画像は同一オリジンと data URI を許可font-src 'self'
:フォントは同一オリジンのみ許可connect-src 'self'
:XHR/WebSocket は同一オリジンのみ許可base-uri 'self'
:<base>
タグは同一オリジンのみ許可form-action 'none'
:フォーム送信を禁止
外部 API を使う場合の CSP
外部 API(例:GitHub API)を使用する場合の設定例です。
html<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.github.com;
"
/>
connect-src
に https://api.github.com
を追加することで、GitHub API への接続を許可しています。
JavaScript での CSP 設定
Main プロセスから HTTP ヘッダーとして CSP を設定することもできます。
typescriptimport { app, BrowserWindow, session } from 'electron';
typescriptapp.whenReady().then(() => {
// CSP をヘッダーとして設定
session.defaultSession.webRequest.onHeadersReceived(
(details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [
"default-src 'self'; script-src 'self'",
],
},
});
}
);
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
},
});
win.loadFile('index.html');
});
この方法では、すべてのレスポンスに CSP ヘッダーを追加できます。HTML ファイルを変更せずに CSP を適用したい場合に便利です。
CSP ディレクティブ早見表
主要な CSP ディレクティブを一覧表にまとめました。
# | ディレクティブ | 用途 | 推奨値 | 説明 |
---|---|---|---|---|
1 | default-src | すべてのリソースのデフォルト | 'self' または 'none' | 他で指定されていないリソースに適用 |
2 | script-src | JavaScript | 'self' | スクリプトの読み込み元を制限 |
3 | style-src | CSS | 'self' 'unsafe-inline' | スタイルシートの読み込み元を制限 |
4 | img-src | 画像 | 'self' data: https: | 画像の読み込み元を制限 |
5 | font-src | フォント | 'self' | フォントの読み込み元を制限 |
6 | connect-src | XHR/WebSocket | 'self' + API ドメイン | 通信先を制限 |
7 | media-src | 音声・動画 | 'self' | メディアの読み込み元を制限 |
8 | object-src | プラグイン | 'none' | <object> などを禁止 |
9 | frame-src | iframe | 'none' または 'self' | iframe の読み込み元を制限 |
10 | base-uri | <base> タグ | 'self' | base URL を制限 |
11 | form-action | フォーム送信先 | 'none' または 'self' | フォーム送信先を制限 |
許可リスト(Allowlist)の設定
特定のリソースへのアクセスを明示的に許可する仕組みです。
プロトコル許可リスト
Electron アプリで使用するカスタムプロトコルや外部プロトコルを制御します。
typescriptimport { app, protocol } from 'electron';
typescriptapp.whenReady().then(() => {
// カスタムプロトコルの登録
protocol.registerFileProtocol(
'app',
(request, callback) => {
const url = request.url.substring(6); // 'app://' を除去
callback({ path: path.join(__dirname, url) });
}
);
// プロトコルの許可リストに追加
protocol.registerSchemesAsPrivileged([
{
scheme: 'app',
privileges: {
standard: true, // 標準プロトコルとして扱う
secure: true, // セキュアなプロトコルとして扱う
supportFetchAPI: true, // Fetch API をサポート
corsEnabled: true, // CORS を有効化
},
},
]);
});
この設定により、app://
プロトコルを安全に使用できるようになります。
ナビゲーション許可リスト
Renderer プロセスが遷移できる URL を制限します。
typescriptimport { app, BrowserWindow } from 'electron';
typescript// 許可する URL のリスト
const ALLOWED_URLS = [
'https://example.com',
'https://api.example.com',
];
typescriptfunction createWindowWithNavGuard() {
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
},
});
// ナビゲーションイベントの監視
win.webContents.on('will-navigate', (event, url) => {
const parsedUrl = new URL(url);
// 許可リストにないドメインへのナビゲーションをブロック
const isAllowed = ALLOWED_URLS.some((allowedUrl) => {
return url.startsWith(allowedUrl);
});
if (!isAllowed) {
console.warn(`ブロックされたナビゲーション: ${url}`);
event.preventDefault();
}
});
return win;
}
will-navigate
イベントをフックすることで、不正な URL への遷移を防ぎます。
新しいウィンドウの許可リスト
window.open()
や <a target="_blank">
による新しいウィンドウの作成を制御します。
typescriptfunction createWindowWithPopupGuard() {
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
},
});
// 新しいウィンドウ作成の監視
win.webContents.setWindowOpenHandler(({ url }) => {
const parsedUrl = new URL(url);
// 許可リストのチェック
const isAllowed = ALLOWED_URLS.some((allowedUrl) => {
return url.startsWith(allowedUrl);
});
if (!isAllowed) {
console.warn(`ブロックされたウィンドウ: ${url}`);
return { action: 'deny' };
}
// 許可された URL はデフォルトブラウザで開く
return {
action: 'allow',
overrideBrowserWindowOptions: {
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
},
},
};
});
return win;
}
setWindowOpenHandler
を使うことで、新しいウィンドウの作成を細かく制御できます。
外部リンクの制御
外部リンクをシステムのデフォルトブラウザで開く設定です。
typescriptimport { shell } from 'electron';
typescriptfunction createWindowWithExternalLinks() {
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
},
});
// 外部リンクをデフォルトブラウザで開く
win.webContents.setWindowOpenHandler(({ url }) => {
// 外部 URL はシステムブラウザで開く
if (
url.startsWith('http://') ||
url.startsWith('https://')
) {
shell.openExternal(url);
return { action: 'deny' };
}
return { action: 'allow' };
});
return win;
}
この設定により、Electron アプリ内で外部サイトを開かず、ユーザーのデフォルトブラウザで開くようになります。
具体例
実践的なセキュア Electron アプリの構成
実際のアプリケーションで使える、完全なセキュリティ設定の実装例を見ていきましょう。
プロジェクト構成
perlmy-electron-app/
├── src/
│ ├── main/
│ │ ├── main.ts # Main プロセス
│ │ └── security.ts # セキュリティ設定
│ ├── preload/
│ │ └── preload.ts # Preload スクリプト
│ └── renderer/
│ ├── index.html # HTML
│ └── renderer.ts # Renderer スクリプト
└── package.json
この構成で、各ファイルの役割を明確に分離します。
Main プロセス(main.ts)
Main プロセスでセキュアな BrowserWindow を作成します。
typescript// src/main/main.ts
import { app, BrowserWindow, ipcMain } from 'electron';
import * as path from 'path';
import { setupSecurity } from './security';
typescript// グローバル参照を保持
let mainWindow: BrowserWindow | null = null;
typescriptfunction createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
// Node.js 統合を無効化
nodeIntegration: false,
nodeIntegrationInWorker: false,
nodeIntegrationInSubFrames: false,
// コンテキスト分離を有効化
contextIsolation: true,
// セキュリティ機能を有効化
webSecurity: true,
allowRunningInsecureContent: false,
// リモートモジュールを無効化
enableRemoteModule: false,
// サンドボックスを有効化
sandbox: true,
// Preload スクリプト
preload: path.join(
__dirname,
'../preload/preload.js'
),
},
});
// HTML ファイルの読み込み
mainWindow.loadFile(
path.join(__dirname, '../renderer/index.html')
);
// ウィンドウが閉じられたときの処理
mainWindow.on('closed', () => {
mainWindow = null;
});
}
typescript// アプリの起動
app.whenReady().then(() => {
// セキュリティ設定を適用
setupSecurity();
// ウィンドウを作成
createWindow();
// macOS での再アクティブ化
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
typescript// すべてのウィンドウが閉じられたときの処理
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
セキュリティ設定(security.ts)
セキュリティ関連の設定を一箇所にまとめます。
typescript// src/main/security.ts
import { app, session, shell } from 'electron';
typescript// 許可する URL のリスト
const ALLOWED_ORIGINS = [
'https://api.github.com',
'https://api.example.com',
];
typescriptexport function setupSecurity() {
// CSP の設定
setupCSP();
// ナビゲーションガード
setupNavigationGuard();
// 新しいウィンドウの制御
setupWindowOpenHandler();
// プロトコルの設定
setupProtocols();
}
typescript// CSP をヘッダーとして設定
function setupCSP() {
session.defaultSession.webRequest.onHeadersReceived(
(details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [
[
"default-src 'self'",
"script-src 'self'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self'",
`connect-src 'self' ${ALLOWED_ORIGINS.join(
' '
)}`,
"object-src 'none'",
"base-uri 'self'",
"form-action 'none'",
].join('; '),
],
},
});
}
);
}
typescript// ナビゲーションガードの設定
function setupNavigationGuard() {
app.on('web-contents-created', (event, contents) => {
contents.on('will-navigate', (navEvent, url) => {
const parsedUrl = new URL(url);
// file:// プロトコルのみ許可
if (parsedUrl.protocol !== 'file:') {
console.warn(
`ブロックされたナビゲーション: ${url}`
);
navEvent.preventDefault();
}
});
});
}
typescript// 新しいウィンドウの制御
function setupWindowOpenHandler() {
app.on('web-contents-created', (event, contents) => {
contents.setWindowOpenHandler(({ url }) => {
// 外部 URL はデフォルトブラウザで開く
if (
url.startsWith('http://') ||
url.startsWith('https://')
) {
shell.openExternal(url);
return { action: 'deny' };
}
return { action: 'deny' };
});
});
}
typescript// カスタムプロトコルの設定
function setupProtocols() {
// app:// プロトコルの特権設定
// これは app.whenReady() の前に呼ぶ必要がある
}
Preload スクリプト(preload.ts)
contextBridge を使って安全に API を公開します。
typescript// src/preload/preload.ts
import { contextBridge, ipcRenderer } from 'electron';
typescript// Renderer に公開する API
contextBridge.exposeInMainWorld('electronAPI', {
// Main プロセスへメッセージ送信
sendMessage: (channel: string, data: any) => {
// 許可されたチャンネルのみ送信可能
const validChannels = ['toMain'];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
// Main プロセスからメッセージ受信
onMessage: (channel: string, callback: Function) => {
// 許可されたチャンネルのみ受信可能
const validChannels = ['fromMain'];
if (validChannels.includes(channel)) {
ipcRenderer.on(channel, (event, ...args) =>
callback(...args)
);
}
},
});
この設定により、Renderer プロセスは window.electronAPI
を通じてのみ Main プロセスと通信できます。
HTML ファイル(index.html)
CSP を含む HTML ファイルの例です。
html<!-- src/renderer/index.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<!-- CSP の設定(二重の保護) -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
/>
<title>セキュアな Electron アプリ</title>
<link rel="stylesheet" href="styles.css" />
</head>
</html>
html<body>
<div id="app">
<h1>セキュアな Electron アプリケーション</h1>
<button id="sendBtn">メッセージ送信</button>
<div id="output"></div>
</div>
<script src="renderer.js"></script>
</body>
</html>
Renderer スクリプト(renderer.ts)
Renderer プロセスで動作するスクリプトです。
typescript// src/renderer/renderer.ts
// TypeScript の型定義
declare global {
interface Window {
electronAPI: {
sendMessage: (channel: string, data: any) => void;
onMessage: (
channel: string,
callback: Function
) => void;
};
}
}
typescript// DOM の初期化
document.addEventListener('DOMContentLoaded', () => {
const sendBtn = document.getElementById('sendBtn');
const output = document.getElementById('output');
// ボタンクリック時の処理
sendBtn?.addEventListener('click', () => {
// Preload で公開した API を使用
window.electronAPI.sendMessage('toMain', {
message: 'Hello from Renderer',
});
});
// Main からのメッセージを受信
window.electronAPI.onMessage('fromMain', (data: any) => {
if (output) {
output.textContent = `受信: ${JSON.stringify(data)}`;
}
});
});
この構成により、Renderer プロセスは直接 Node.js API にアクセスできず、contextBridge
で公開された API のみを使用します。
セキュリティ検証の実施
実装したセキュリティ設定が正しく動作しているか検証しましょう。
DevTools でのテスト
開発者ツールのコンソールで以下を実行してみてください。
javascript// Node.js API へのアクセステスト(失敗するはず)
typeof require; // => 'undefined'
typeof process; // => 'undefined'
typeof __dirname; // => 'undefined'
javascript// contextBridge で公開した API のテスト(成功するはず)
typeof window.electronAPI; // => 'object'
window.electronAPI.sendMessage('toMain', { test: true }); // => 動作する
すべてのテストで期待通りの結果が得られれば、セキュリティ設定は正しく機能しています。
CSP 違反の確認
DevTools のコンソールに CSP 違反のエラーが表示されないか確認します。
javascript// インラインスクリプトのテスト(ブロックされるはず)
eval('console.log("test")');
// => Refused to evaluate a string as JavaScript because 'unsafe-eval'...
CSP が正しく設定されていれば、eval()
などの危険な関数は実行できません。
まとめ
Electron アプリケーションのセキュリティは、webPreferences
、CSP、許可リストの 3 つの柱で構成されます。
webPreferences の重要設定
nodeIntegration: false
で Node.js API への直接アクセスを禁止contextIsolation: true
でコンテキストを分離sandbox: true
で Chromium サンドボックスを有効化
CSP の活用
default-src 'self'
ですべてのリソースを同一オリジンに制限- 必要な外部リソースのみ明示的に許可
- HTML とヘッダーの両方で設定し二重に保護
許可リストによる制御
- ナビゲーション先を制限し、不正な URL への遷移を防止
- 新しいウィンドウの作成を制御
- 外部リンクはシステムブラウザで開く
これらの設定を組み合わせることで、XSS 攻撃やリモートコード実行などの脅威から Electron アプリを守ることができます。本記事で紹介したチートシートを参考に、ぜひセキュアなアプリケーション開発を実践してください。
セキュリティは一度設定すれば終わりではなく、Electron のバージョンアップや新たな脅威に応じて継続的に見直す必要があります。定期的に公式ドキュメントを確認し、最新のベストプラクティスを取り入れていきましょう。
関連リンク
- article
Electron セキュリティ設定チートシート:webPreferences/CSP/許可リスト早見表
- article
Electron セットアップ最短ルート:Vite + TypeScript + ESLint + Preload 分離
- article
Electron vs Tauri vs Flutter Desktop:サイズ/速度/互換を実測比較
- article
Electron トラブルシュート:白画面(White Screen)問題を 3 分で切り分け
- article
Electron 入門 2025:Web 技術でデスクトップアプリを作る全体像
- article
TauriとElectronのパフォーマンス比較
- article
NotebookLM とは?Google 製 AI ノートの仕組みとできることを 3 分で解説
- article
Mermaid 図面の命名規約:ノード ID・エッジ記法・クラス名の統一ガイド
- article
Emotion 初期設定完全ガイド:Babel/SWC/型定義/型拡張のベストプラクティス
- article
Electron セキュリティ設定チートシート:webPreferences/CSP/許可リスト早見表
- article
MCP サーバー とは?Model Context Protocol の基礎・仕組み・活用メリットを徹底解説
- article
Yarn とは?npm・pnpm と何が違うのかを 3 分で理解【決定版】
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来