Electron でデスクトップアプリを作っていると、ローカルに保存する SQLite データベースを暗号化しておきたい場面がある。認証情報・顧客データ・下書き中の業務データなど、端末の盗難や別プロセスからのファイル走査に対して素の SQLite ファイルを置きたくない、というよくある要件である。
問題は「SQLite 本体には Data at Rest 用の暗号化機能が組み込まれていない」という点で、Electron から扱うにはいわゆる暗号化 SQLite ライブラリを別途選ぶ必要がある。この記事では、Electron からローカル SQLite を暗号化する目的で候補になるライブラリを、本番で採用するときに確認する観点(暗号方式・N-API プリビルドの有無・ライセンス・macOS 署名と公証との相性)で比較する。
先に結論を書くと、2026 年時点では better-sqlite3-multiple-ciphers を第一候補、@signalapp/sqlcipher と SQLite Encryption Extension(SEE) を条件付きの第二・第三候補、@journeyapps/sqlcipher はメンテナンスは継続しているものの v6.0.0 で Windows サポートを廃止しているため Windows もターゲットに含む Electron アプリでは選びづらい、というのが本記事の推奨である。
Electron でローカル DB を暗号化する意味
Electron は Main プロセス(Node.js 側)と Renderer プロセス(Chromium 側)に分かれたマルチプロセス構成を取る。Renderer からは IndexedDB や LocalStorage を素直に使えるが、これらはユーザーデータディレクトリ配下に平文で保存される。端末の物理盗難、同一 OS ユーザー権限で走る別プロセス、マルウェアによるファイルシステム走査に対しては、そのままだと無防備である。
そこで、業務用の Electron アプリでは、SQLite ファイルそのものをページ単位で暗号化するアーキテクチャがよく採用される。これは Transparent Data Encryption(TDE) と呼ばれる考え方で、代表的な実装が SQLCipher と SQLite3 Multiple Ciphers である。どちらも SQLite のページ単位で暗号化・復号を行うため、アプリのコード側はほぼ通常の SQLite として扱える。
比較対象を整理する
Electron から使える SQLite / ローカル DB 系ライブラリはたくさんあるが、暗号化対応の有無で二分できる。
暗号化非対応(本記事では詳しく扱わない):
better-sqlite3: 同期 API・N-API プリビルド・活発なメンテという意味で、暗号化不要ケースの第一候補。v13 で N-API 移行済みsqlite3(node-sqlite3): 非同期 API の伝統的なバインディング。2026 年 7 月にリポジトリが archive され read-only になったため、新規採用は避けたいnode:sqlite: Node.js 22 以降の組み込みモジュール。ネイティブ再ビルド不要という強みはあるが、暗号化機能を含まないsql.js: SQLite を WebAssembly に移植したもの。DB イメージ全体をメモリに置く方式で、大容量・機密データには不利lmdb-js: LMDB の Node バインディング。KV ストアとして高速だが SQL も暗号化も持たない
暗号化対応(本記事のメイン):
better-sqlite3-multiple-ciphers: better-sqlite3 のフォーク。SQLite3 Multiple Ciphers による暗号化。SQLCipher 互換モードも搭載@journeyapps/sqlcipher: node-sqlite3 のフォーク。本家 SQLCipher と OpenSSL をバンドル@signalapp/sqlcipher: Signal Messenger が 2025 年に公開した新しい N-API ベースの SQLCipher アドオン- SQLite Encryption Extension(SEE): SQLite 本家が提供する商用暗号化拡張
- Realm: SQLite ではないが、暗号化対応のローカル DB としてしばしば比較対象になる
以下では暗号化対応組を中心に、Electron でどれを選ぶかを掘り下げる。
better-sqlite3-multiple-ciphers
better-sqlite3-multiple-ciphers は、同期 API で高速な better-sqlite3 をフォークし、SQLite3 Multiple Ciphers 拡張を組み込んだネイティブモジュールである。既定の暗号方式は ChaCha20-Poly1305、他に AES-256-CBC・SQLCipher v1〜v4 互換の暗号方式に対応している。作者の m4heshd は上流 better-sqlite3 のメンテナも兼任しており、N-API 移行を含む上流の変更を密に追従している。
Electron で採用する場合の利点は以下である。
- v13.0.0 で N-API に移行済み。従来の Electron ABI 別プリビルド方式(
electron-v121・electron-v123などをリリースごとに 100 個近く用意する形)から、N-API 単一バイナリを npm パッケージに同梱する方式に変わった。上流 better-sqlite3 のリリースノート原文は “Version 13.0.0 marks a major milestone, as it’s the first version of better-sqlite3 to run on the N-API. This means prebuilt binaries should theoretically work across different versions of Node.js and Electron, and perhaps even other runtimes like Bun.” となっており、Electron バージョンアップのたびに再ビルドする摩擦の削減が意図されている - プリビルドが全主要プラットフォーム・アーキテクチャ(Windows x86 / x64 / arm64、macOS x64 / arm64、Linux glibc・musl の x64 / arm / arm64)を網羅している。electron-builder や Electron Forge が自動でダウンロードしてくれる
- 同期 API なので V8 と SQLite C API の往復オーバーヘッドが小さく、単純なクエリで
node-sqlite3比 11.7 倍〜15.6 倍という ベンチマーク が公開されている(2020 年時点の環境のため参考値扱いだが、傾向としては現在も同じ) - MIT ライセンスで、SQLite3 Multiple Ciphers 側も MIT。追加のアプリ内表示義務は生じない
注意点もある。
- 「理論上(theoretically)」動作するはず、というのは上流 README の表現である。実プロジェクトのターゲット(特に macOS arm64 + universal、Windows arm64)で再ビルドなしに動くかは実測で必ず検証したい
- SQLite3 Multiple Ciphers 既定形式で作った DB は DB Browser for SQLite で開けない場合がある。同じく SQLite3 Multiple Ciphers を使う SQLiteStudio を用意しておくと運用時に楽になる
- SQLCipher 形式の DB を開くには
db.pragma("cipher='sqlcipher'")とdb.pragma("legacy=4")を明示的に指定する
暗号鍵を設定する典型的なコード例は次のようになる。
import Database from 'better-sqlite3-multiple-ciphers'
const db = new Database('app.db')
db.pragma(`key = '${passphrase}'`)
db.exec(`CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY,
body TEXT NOT NULL
)`)SQLCipher 互換 DB を開く場合は次のように事前に PRAGMA を投げる。
const db = new Database('legacy-sqlcipher.db')
db.pragma("cipher = 'sqlcipher'")
db.pragma("legacy = 4")
db.pragma(`key = '${passphrase}'`)@journeyapps/sqlcipher
@journeyapps/sqlcipher は歴史的によく使われてきた選択肢で、node-sqlite3 をベースに SQLCipher と OpenSSL をバンドルしている。長らく v5.3.1(2022 年 1 月)で更新が止まっていたが、2026 年 4 月にメンテナンス目的の v6.0.0 がリリースされ、再び動いている状態である。ただし v6.0.0 の変更内容がクロスプラットフォーム Electron との相性を大きく崩しているため、Windows もターゲットに含む Electron アプリでは候補から外れる。
- v6.0.0 で Windows サポートを完全に廃止した。リリースノートには “Drop Windows support and switch the package to source-build-only installs on macOS and Linux” と明記されており、Windows 版 Electron アプリの候補としては最初から外れる
- 同じ v6.0.0 で node-pre-gyp とプリビルドバイナリ配布を廃止し、macOS / Linux もソースビルド専用になった。ユーザー側 CI やインストーラで SQLCipher / OpenSSL のビルドチェーンを持たないと
npm installすら通らない - v5 系では M1 / M2 Mac での問題が複数プロジェクトで報告されており、capacitor-community を含むいくつかのプロジェクトは本ライブラリから
better-sqlite3-multiple-ciphersへ移行済みである - Electron Forge がデフォルトでソースから再ビルドしようとして node-pre-gyp 構成と衝突する既存の問題(回避に
config.forge.electronRebuildConfig.onlyModules: []が必要)は、v6.0.0 でソースビルドオンリー化されたため前提が変わっているが、Electron ABI に合わせた再ビルド設定を自前で持つ手間は残る
すでに @journeyapps/sqlcipher を使っている既存プロジェクトなら、DB ファイル形式互換を保ったまま better-sqlite3-multiple-ciphers の SQLCipher 互換モード(cipher='sqlcipher' + legacy=4)へ切り替える移行を計画しておきたい。特に Windows もターゲットに含む Electron アプリなら、v6.0.0 の登場は「更新は続いているものの、自分たちのユースケースからは離れていっている」と読むのが素直である。
@signalapp/sqlcipher
@signalapp/sqlcipher は、Signal Messenger が 2025 年に公開した比較的新しい N-API ベースの SQLCipher アドオンである。中身は本家 SQLCipher v4.10.0 を採用しており、Signal Desktop 本体で実運用されている実績を持つ。
Electron アプリの選定上、意識すべきポイントは主にライセンスである。
- ライセンスは AGPL-3.0-only。README にも “Copyright 2025 Signal Messenger, LLC. Licensed under the AGPLv3” と明記されている。クローズドソースの商用デスクトップアプリに組み込む場合は、AGPL の伝播が問題になる可能性が非常に高い。採用検討時は必ず法務レビューを通したい
- プリビルドの網羅性は Signal 自社のターゲット中心なので、自プロジェクトのターゲット(Windows arm64 など)で使えるかは要検証
- Signal Desktop 固有の FTS5 セグメンテーション API など、汎用アプリからは使わない拡張が同梱されている
「本家 SQLCipher の暗号方式(AES-256-CBC + HMAC-SHA512)を Signal と同じ実装で使いたい、かつ AGPL が受容できる」ケース限定の選択肢である。
SQLite Encryption Extension(SEE)
SQLite Encryption Extension は SQLite 本家(hwaci)が提供する商用暗号化拡張である。AES-256-OFB・AES-128-OFB・AES-128-CCM・AES-256-GCM といった複数の暗号方式をサポートし、SQLite 本体との互換性は極めて高い。
Electron から使う場合は、better-sqlite3 の公式ドキュメント が明示している custom amalgamation ビルドで、sqlite3.c / sqlite3.h を SEE のソースへ差し替えて組み込むのが素直な経路である。
npm install better-sqlite3 \
--build-from-source \
--sqlite3=/path/to/see-amalgamationSEE の位置付けは以下のように整理できる。
- ライセンスは永久ソースライセンスで US$2,000。年額課金の SQLCipher Commercial(US$999 / アプリ / 年〜) と TCO の形が違うため、寿命の長いデスクトップ製品では固定費モデルが魅力になり得る
- SEE ソース自体の再配布は不可。コンパイル済み成果物としてアプリに同梱する形になる
- SEE 公式は新規開発向けに AES-256-OFB を推奨しているが、OFB は認証付き暗号ではない。DB ファイルの改ざんが脅威モデルに含まれるなら、AES-256-GCM または AES-128-CCM を明示的に選ぶ設計が望ましい
- 公式資料自身が「メモリ上では暗号化されていない」と明記している。SQLCipher と同様、平文がメモリに出るのは避けられない
Realm
Realm は C++ 実装のオブジェクト DB で、64-byte のキーによるファイル暗号化を標準サポートする。SQLite 互換ではなく独自クエリ API なので、SQL 資産をそのまま持ち込みたい場合には向かない。
Electron 目線で押さえておきたいのは次の点である。
- 2024 年に Atlas Device Sync および Realm SDK の deprecation が告知 された。2024 年の Realm JS v20 では Device Sync 関連機能が削除された。「オフライン同期を Realm に任せる」という過去の選定理由は 2026 年時点では成立しない
- Realm は「Thread Affinity(スレッド束縛)」の制約があり、Main / Renderer / Worker をまたぐ Electron の IPC 構成と組み合わせるとオブジェクトの無効化とシリアライズが頻発する
- Electron の複数ウィンドウから暗号化 Realm を並行操作した際のクラッシュ報告が GitHub issue にある。CVE ではないため深刻度の判断は分かれるが、セキュリティクリティカルな採用ではワークロード再現テストを実施したい
「既存のモバイルアプリ(React Native 等)で Realm を使っていて、デスクトップ版とドメインモデルを共有したい」という具体的な要件があるとき以外は、暗号化 SQLite を第一候補にした方が Electron との整合は取りやすい。
比較表
主要な観点でまとめると次のようになる。
| 項目 | better-sqlite3-multiple-ciphers | @journeyapps/sqlcipher | @signalapp/sqlcipher | SQLite SEE | Realm JS |
|---|---|---|---|---|---|
| 暗号エンジン | SQLite3 Multiple Ciphers | 本家 SQLCipher + OpenSSL | 本家 SQLCipher v4.10.0 | SQLite 本家 SEE | Realm Core |
| 既定の暗号方式 | ChaCha20-Poly1305 | AES-256-CBC + HMAC-SHA512 | AES-256-CBC + HMAC-SHA512 | AES-256-OFB(推奨) | AES-256 |
| 改ざん検知 | 方式による(Poly1305 は AEAD) | 標準(HMAC-SHA512) | 標準(HMAC-SHA512) | GCM / CCM を選べば強い | 独自実装 |
| SQL 互換性 | SQLite ほぼ完全互換 | SQLite ほぼ完全互換 | SQLite ほぼ完全互換 | SQLite ほぼ完全互換 | 独自クエリ |
| API | 同期 | 非同期(コールバック) | 同期 | バインディングによる | 独自 |
| プリビルド網羅性 | 全主要プラットフォーム・アーキテクチャ | v6.0.0 でプリビルド廃止(ソースビルドのみ) | Signal 中心 | 自前ビルド前提 | 主要のみ |
| 対応プラットフォーム | Windows・macOS・Linux | v6.0.0 で Windows サポート廃止(macOS・Linux のみ) | Signal ターゲット中心 | 自前ビルド次第 | 主要 OS |
| N-API 対応 | v13 で対応 | 対応済み | 対応済み | バインディングによる | 対応済み |
| メンテ状況 | 活発(v13.0.3、2026-08) | 継続(v6.0.0、2026-04。Windows 対応廃止) | Signal 主導で更新 | SQLite 本家が継続 | Sync 廃止後は縮小 |
| ライセンス | MIT | BSD-3-Clause | AGPL-3.0-only | 商用(US$2,000 永久) | Apache-2.0 |
| クローズド商用適合 | 適合 | 適合 | 要法務レビュー | 適合 | 適合 |
ネイティブモジュールの起動・署名・公証の制約
ライブラリの選定に加えて、Electron のネイティブモジュールに共通するビルド・署名・公証の設定が必要になる。設定が適切でなければ、起動時に Error: Module did not self-register が発生したり、macOS の公証に失敗したりする。
.node バイナリを asar から unpack する
Electron の asar アーカイブ はネイティブモジュールをそのままロードできない。.node バイナリは app.asar.unpacked 側へ展開する必要がある。electron-builder なら package.json に次のように書く。
{
"build": {
"asarUnpack": ["**/*.node"]
}
}Electron Forge なら @electron-forge/plugin-auto-unpack-natives を使う。近年の electron-builder はネイティブモジュールを自動検出して unpack するが、明示設定を残しておく方が安全である。
macOS の Hardened Runtime と公証
公証(notarization)には Hardened Runtime が必須である。electron-builder の場合、次のように設定する。
{
"build": {
"mac": {
"hardenedRuntime": true,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist"
}
}
}entitlements 側では JIT を許可する。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>ヘルパープロセスから Team ID の異なる .node をロードしなければならないケースでは、com.apple.security.cs.disable-library-validation の付与が必要になることもある。同クラスの library validation 起因のロード失敗事例は近年もデスクトップアプリで散発的に報告されており、better-sqlite3 系も同じクラスの問題に巻き込まれ得る。
universal ビルドの罠
macOS の universal(x64 + arm64)ビルドを electron-builder で作ると、asar 内部が app-x64.asar と app-arm64.asar に分割され、その上にルーティング用の index.js が挿入される。.app の内部構造が変わるため、loadFile のパス解決や .node の位置に前提を置くコードが壊れる報告がある。ユニバーサル配布を諦めて arm64 / x64 を別ビルドとして配布する選択肢も、Electron アプリでは十分現実的である。
Windows 署名
.node は Windows でも signtool で個別署名される。electron-builder v24 で Mac 上から Windows 向け .node を osslsigncode で署名する際に「Unrecognized file type」エラーが出る事例が報告されている(Issue #7652 / #7655)。electron-builder v27 では Windows 署名設定が win.sign の discriminated union(signtool / hsm / pkcs11 / azure)に再編されており、旧 win.signtoolOptions は migrate-schema で自動変換できる。
暗号鍵はどこに置くか
ライブラリ選定と同じくらい重要なのが、DB を開くためのパスフレーズや raw key をどこに保存するかである。ハードコーディングや平文設定ファイルへの書き出しは、リバースエンジニアリングやファイル解析で容易に鍵を抜かれるため論外である。
Electron には safeStorage API があり、OS ごとの保護されたストレージへ透過的に鍵を書き出せる。
- macOS: Keychain を利用
- Windows: DPAPI(Data Protection API) を利用
- Linux: Secret Service API(GNOME Keyring や KWallet)を利用
Linux では GUI セッションが存在しない環境や、Secret Service が利用できない環境で平文フォールバックが発生し得る点だけ注意しておきたい。
典型的なフローは次のようになる。
flowchart TB
Init[初回起動] --> Gen[CSPRNG で 256-bit マスターキー生成]
Gen --> Enc[safeStorage.encryptString で暗号化]
Enc --> Save[ユーザーデータ配下に保存]
Boot[以降の起動] --> Dec[safeStorage.decryptString で復号]
Dec --> Pragma["PRAGMA key = '...' で DB を open"]
Pragma --> Query[Renderer からの IPC を Main で処理]
重要な設計原則は「復号された暗号鍵と DB ハンドルは Main プロセス(または専用の utility process)に閉じ込め、Renderer には IPC 経由の抽象化されたクエリ API だけを露出する」ことである。Renderer に鍵を渡すと、XSS 経由での漏洩リスクを丸ごと抱え込むことになる。
safeStorage は同一 OS ユーザー権限で走る別の悪意あるプロセスや、同一プロセス内へのコードインジェクションまでは守れない。より高い保護水準が必要な用途(金融・暗号資産ウォレット等)では、ユーザー自身のマスターパスワードから OWASP ガイドライン に沿った鍵導出関数(PBKDF2 を 600,000 回以上、または Argon2id)で動的に鍵を復元する設計と組み合わせるのが標準的である。
ライセンス整理
暗号化ライブラリの選定は、法務レビューの通しやすさも重要な観点になる。
- SQLite3 Multiple Ciphers 本体・
better-sqlite3-multiple-ciphersバインディング: MIT。追加のアプリ内表記義務は発生しない - SQLCipher Community Edition: BSD 系。商用利用可能だが、アプリと配布物にライセンス本文と
Copyright (c) 2008-2026, ZETETIC, LLC表記をユーザーがアクセスできる場所に掲示する義務がある - SQLCipher Commercial Edition: US$999 / アプリ / 年〜。プリビルドパッケージ・優先サポート・Community 比で最大 4 倍高速な暗号処理が提供される。FIPS 140-3 が必要なら Enterprise 版
- SQLite SEE: US$2,000 永久ソースライセンス
@signalapp/sqlcipher: AGPL-3.0-only。クローズドソース商用アプリでは通常不適合- Realm JS: Apache-2.0
何を選ぶか
以上を踏まえた推奨は次のようになる。
第一段階として、標準的な暗号化ニーズを持つクローズド商用の Electron アプリなら better-sqlite3-multiple-ciphers を採用する。理由はプリビルド網羅性・N-API による Electron バージョン非依存化・活発なメンテナンス・MIT ライセンス・同期 API による低レイテンシがバランスよく揃っているためである。
第二段階として、本家 SQLCipher の実装を Signal と同じコードベースで使う必要があり、かつライセンスが AGPL でも問題ないなら @signalapp/sqlcipher を評価する。ただし AGPL の適合性チェックは必須。
第三段階として、SQLite 本家の商用サポートが必要、もしくは AES-256-GCM のように認証付き暗号を明示的に選びたいなら SQLite SEE を購入する。永久ライセンスモデルは長寿命製品では TCO が読みやすい。
クロスプラットフォームの Electron アプリで使う暗号化 SQLite としては、@journeyapps/sqlcipher と、node-sqlite3 をベースにした自前 SQLCipher ビルド構成は候補から外れる。前者は 2026 年 4 月の v6.0.0 で Windows サポートとプリビルドを廃止したため、Windows を含むターゲットには使えない(macOS / Linux 限定の Electron アプリで、ソースビルド用のツールチェーンを CI 側で持てるなら選択肢としては残る)。後者は 2026 年 7 月のリポジトリ archive が理由である。既存プロジェクトが Windows もターゲットに含むかたちで前者に依存しているなら、better-sqlite3-multiple-ciphers への移行計画を早めに立てておきたい。
Realm は「モバイルアプリ側で既に Realm を採用していてドメインモデルを共有したい」という具体的な要件があるとき以外は、Electron アプリの新規標準としては候補から外して良い。Sync 廃止後の位置付けと Thread Affinity の制約が、Electron のマルチプロセスモデルと相性が悪い。
判断が変わる閾値も明示しておきたい。
better-sqlite3-multiple-ciphersv13 の N-API プリビルドが自プロジェクトのターゲット(特に macOS arm64 + universal、Windows arm64)で再ビルドなしに動作確認できれば、第一段階で確定してよい- 動作しない・公証で弾かれる場合、まずはライブラリを変える前に
.nodeの asar unpack と entitlements(allow-jit、必要ならdisable-library-validation)の設定を疑う。ライブラリ固有ではなくネイティブモジュール共通の問題である場合が多い - Community Edition の BSD attribution 要件や AGPL がビジネス上受容できない場合は、第三段階(SEE の商用ライセンス)へ
以上、Electron でローカル SQLite を暗号化する際のライブラリ候補を比較し、なぜ better-sqlite3-multiple-ciphers を本命に据えるのか、そしてネイティブモジュール共通の署名・公証・鍵管理の制約までを整理した、現場からお送りしました。
参考情報
- better-sqlite3-multiple-ciphers
- SQLite3 Multiple Ciphers 公式サイト
- WiseLibs/better-sqlite3
- SQLCipher(Zetetic)
- SQLite Encryption Extension(SEE)
- @signalapp/sqlcipher
- @journeyapps/sqlcipher(node-sqlcipher リポジトリ)
- Electron safeStorage API
- Electron asar アーカイブ
- Notarizing macOS software before distribution
- OWASP Password Storage Cheat Sheet