量子耐性メール:暗号化されたSQLiteメールボックスを使ってあなたのメールを安全に保つ方法

量子安全な暗号化メールサービスのイラスト

序文

Important

私たちのメールサービスは100%オープンソースであり、安全かつ暗号化されたSQLiteメールボックスを通じてプライバシーに配慮しています。

IMAPサポートを開始するまでは、永続的なデータストレージにはMongoDBを使用していました。

この技術は素晴らしく、現在も使用していますが、MongoDBでの保存時暗号化を利用するには、Digital OceanやMongo AtlasのようなMongoDB Enterpriseを提供するプロバイダーを使うか、エンタープライズライセンスを購入する必要があります(その場合、営業チームとのやり取りに時間がかかります)。

Forward Emailのチームは、IMAPメールボックス用に開発者に優しく、スケーラブルで信頼性が高く、暗号化されたストレージソリューションを必要としていました。オープンソース開発者として、保存時暗号化機能を得るためにライセンス料を支払う必要がある技術を使うことは私たちの原則に反するため、これらのニーズを解決するために新しいソリューションを一から研究・開発しました。

共有データベースを使ってメールボックスを保存する代わりに、私たちはあなたのパスワード(あなただけが知っている)で個別にメールボックスを保存し暗号化しています。私たちのメールサービスは非常に安全で、パスワードを忘れるとメールボックスを失います(オフラインバックアップで復元するか、最初からやり直す必要があります)。

以下で、メールサービスプロバイダーの比較サービスの仕組み技術スタックなどを詳しく解説します。

メールサービスプロバイダー比較

私たちは、個別に暗号化されたSQLiteメールボックスを保存し、無制限のドメイン、エイリアス、ユーザーを提供し、送信SMTP、IMAP、POP3をサポートする唯一の100%オープンソースかつプライバシー重視のメールサービスプロバイダーです。

他のメールプロバイダーとは異なり、Forward Emailではドメインやエイリアスごとにストレージ料金を支払う必要はありません。 ストレージはアカウント全体で共有されるため、複数のカスタムドメイン名やそれぞれに複数のエイリアスがある場合に最適なソリューションです。必要に応じて、ドメインやエイリアスごとにストレージ制限を設定することも可能です。

メールサービス比較を読む

仕組み

  1. Apple Mail、Thunderbird、Gmail、Outlookなどのメールクライアントを使い、ユーザー名とパスワードで私たちの安全なIMAPサーバーに接続します:

    • ユーザー名は hello@example.com のようにドメインを含むフルエイリアスです。
    • パスワードはランダムに生成され、 パスワードを生成をクリックすると30秒間だけ表示されます。場所はマイアカウント ドメイン エイリアスです。
  2. 接続が確立されると、あなたのメールクライアントはIMAPプロトコルコマンドを当社のIMAPサーバーに送信し、メールボックスを同期状態に保ちます。これには、下書きメールの作成・保存や、メールに「重要」ラベルを付けたり、スパム/迷惑メールとしてフラグを立てたりするなどの操作が含まれます。

  3. メール交換サーバー(一般に「MX」サーバーと呼ばれます)は、新しい受信メールを受け取り、あなたのメールボックスに保存します。これが行われると、あなたのメールクライアントに通知が届き、メールボックスが同期されます。当社のメール交換サーバーは、あなたのメールを1人以上の受信者(webhooksを含む)に転送したり、暗号化されたIMAPストレージに保存したり、その両方を行うことができます

    Tip

    詳しく知りたいですか?メール転送の設定方法当社のメール交換サービスの仕組み、または当社のガイドをお読みください。

  4. 裏側では、当社の安全なメールストレージ設計は、あなたのメールボックスを暗号化し、あなただけがアクセスできるようにするために2つの方法で機能しています:

    • 送信者からあなた宛に新しいメールが届くと、当社のメール交換サーバーはあなた専用の一時的で暗号化された個別のメールボックスに書き込みます。

    • あなたがメールクライアントで当社のIMAPサーバーに接続すると、あなたのパスワードはメモリ内で暗号化され、メールボックスの読み書きに使用されます。メールボックスはこのパスワードでのみ読み書き可能です。あなたがこのパスワードを唯一知っているため、あなた以外はアクセスできません。次にメールクライアントがメールのポーリングや同期を試みる際、新しいメッセージはこの一時的なメールボックスから転送され、あなたが提供したパスワードを使って実際のメールボックスファイルに保存されます。この一時的なメールボックスはその後消去・削除されるため、メッセージはパスワードで保護されたメールボックスにのみ存在します。

    • IMAPに接続している場合(例:Apple MailやThunderbirdなどのメールクライアントを使用している場合)、一時的なディスクストレージに書き込む必要はありません。代わりに、メモリ内で暗号化されたIMAPパスワードが取得され使用されます。リアルタイムでメッセージが配信されようとするとき、すべてのIMAPサーバーに対してWebSocketリクエストを送り、あなたのアクティブなセッションがあるか確認します(これが取得部分です)。その後、暗号化されたメモリ内パスワードを渡すため、一時的なメールボックスに書き込む必要はなく、暗号化されたパスワードを使って実際の暗号化メールボックスに書き込むことができます。

  5. 暗号化されたメールボックスのバックアップは毎日作成されます。いつでも新しいバックアップをリクエストしたり、マイアカウント ドメイン エイリアスから最新のバックアップをダウンロードすることも可能です。別のメールサービスに切り替える場合でも、メールボックスやバックアップの移行、ダウンロード、エクスポート、削除をいつでも簡単に行えます。

技術

データベース

他のデータベースストレージ層も検討しましたが、SQLiteほど当社の要件を満たすものはありませんでした:

Database Encryption-at-rest Sandboxed Mailboxes License Used Everywhere
SQLite SQLite3MultipleCiphers で対応済み ✅ パブリックドメイン
MongoDB "MongoDB Enterpriseのみで利用可能" ❌ リレーショナルデータベース ❌ AGPL および SSPL-1.0
rqlite ネットワークのみ ❌ リレーショナルデータベース MIT
dqlite 未検証かつ未対応? 未検証かつ未対応? LGPL-3.0-only
PostgreSQL 対応済み ❌ リレーショナルデータベース PostgreSQLBSD または MIT に類似)
MariaDB InnoDBのみ対応 ❌ リレーショナルデータベース GPLv2 および BUSL-1.1
CockroachDB Enterprise限定機能 ❌ リレーショナルデータベース BUSL-1.1 など

こちらは上記の表にある複数のSQLiteデータベースストレージオプションを比較したブログ記事です。

セキュリティ

常に encryption-at-restAES-256)、encryption-in-transitTLS)、🍊 Tangerine を使った DNS over HTTPS(「DoH」)、およびメールボックスに対して sqleetChaCha20-Poly1305)暗号化を使用しています。さらに、トークンベースの二要素認証(中間者攻撃の影響を受けやすいSMSではなく)、ルートアクセスを無効化したSSHキーのローテーション、制限されたIPアドレスからのサーバーへの排他的アクセスなども実施しています。 悪意のあるメイド攻撃やサードパーティのベンダーからの不正な従業員が発生した場合でも、あなたのメールボックスはあなたが生成したパスワードでのみ開くことができます。ご安心ください。当社はCloudflare、DataPacket、Digital Ocean、GitHub、VultrのSOCタイプ2準拠のサーバープロバイダー以外のサードパーティベンダーには依存していません。

当社の目標は、単一障害点をできるだけ少なくすることです。

メールボックス

要約; 当社のIMAPサーバーは、各メールボックスごとに個別に暗号化されたSQLiteデータベースを使用しています。

SQLiteは非常に人気のある組み込みデータベースで、現在あなたのスマートフォンやコンピューターで動作しており、ほぼすべての主要技術で使用されています

例えば、当社の暗号化されたサーバー上には、linux@example.cominfo@example.comhello@example.comなどのSQLiteデータベースメールボックスがあり、それぞれが.sqliteデータベースファイルとして存在します。データベースファイル名はメールアドレスではなく、BSON ObjectIDやユニークなUUIDを使用しており、どのメールボックスに属するかやメールアドレスが何であるかは特定できません(例:353a03f21e534321f5d6e267.sqlite)。

これらの各データベースは、あなたのパスワード(あなただけが知っている)を使ってsqleetChaCha20-Poly1305)で暗号化されています。つまり、あなたのメールボックスは個別に暗号化され、自己完結型で、サンドボックス化されており、ポータブルです。

当社は以下のPRAGMAでSQLiteを最適化しています:

PRAGMA 目的
cipher=chacha20 ChaCha20-Poly1305 SQLiteデータベース暗号化。詳細はProjectsbetter-sqlite3-multiple-ciphersを参照してください。
key="****************" これはあなたのメールクライアントのIMAP接続を通じて当社サーバーに渡される、メモリ上でのみ復号されたパスワードです。読み書きセッションごとに新しいデータベースインスタンスが作成・閉鎖され、サンドボックス化と分離を保証します。
journal_mode=WAL 書き込み先行ログ("WAL") でパフォーマンスを向上させ、同時読み取りアクセスを可能にします
busy_timeout=5000 書き込み中のロックエラーを防止します。他の書き込みが行われている間
synchronous=NORMAL トランザクションの耐久性を高め、データ破損のリスクを減らします
foreign_keys=ON 外部キー参照(例:あるテーブルから別のテーブルへの関係)を強制します。SQLiteではデフォルトで無効ですが、検証とデータ整合性のために有効にすべきです。
encoding='UTF-8' 開発者の利便性を確保するためのデフォルトエンコーディング

その他のデフォルト設定は、公式PRAGMAドキュメントに記載されているSQLiteの仕様に準じます。

同時実行

要約; 暗号化されたSQLiteメールボックスへの同時読み書きには WebSocket を使用しています。

読み取り

携帯のメールクライアントは imap.forwardemail.net を当社のDigital OceanのIPアドレスのいずれかに解決することがあり、デスクトップクライアントは別のプロバイダーの異なるIPを解決する場合があります。

どのIMAPサーバーに接続しても、データベースからリアルタイムかつ100%正確に読み取れる接続を目指しています。これはWebSocketを通じて実現しています。

書き込み

データベースへの書き込みは少し異なります。SQLiteは組み込みデータベースであり、メールボックスはデフォルトで単一ファイルに格納されているためです。

litestreamrqlitedqliteなどの選択肢を検討しましたが、いずれも要件を満たしませんでした。

書き込み先行ログ("WAL")を有効にした状態で書き込みを行うには、1台のサーバー(「プライマリ」)のみが担当する必要があります。WALは同時実行を大幅に高速化し、1つの書き込みと複数の読み取りを可能にします。

プライマリは暗号化されたメールボックスを含むマウント済みボリュームを持つデータサーバー上で稼働しています。配布の観点からは、imap.forwardemail.netの背後にある個々のIMAPサーバーはすべてセカンダリサーバー(「セカンダリ」)と考えることができます。

双方向通信はWebSocketsで実現しています:

  • プライマリサーバーはwsWebSocketServer サーバーインスタンスを使用。
  • セカンダリサーバーはwsWebSocket クライアントインスタンスを使用し、websocket-as-promisedreconnecting-websocket でラップしています。これら2つのラッパーは WebSocket の再接続を保証し、特定のデータベース書き込みのための送受信を可能にします。

バックアップ

要約; 暗号化されたメールボックスのバックアップは毎日作成されます。いつでも マイアカウント ドメイン エイリアス から新しいバックアップの即時リクエストや最新バックアップのダウンロードが可能です。

バックアップは、IMAPコマンド処理中に毎日SQLiteの VACUUM INTO コマンドを実行して行います。これはメモリ内IMAP接続からの暗号化パスワードを利用しています。既存のバックアップが検出されない場合、またはファイルのSHA-256ハッシュが最新バックアップと異なる場合にバックアップを保存します。

backup コマンドではなく VACUUM INTO コマンドを使用する理由は、backup コマンド実行中にページが変更されると最初からやり直す必要があるためです。VACUUM INTO コマンドはスナップショットを取得します。詳細はGitHubHacker Newsのコメントを参照してください。

さらに、backup コマンドは rekey が呼ばれるまでの間、データベースが一時的に暗号化されていない状態になるため、VACUUM INTO を使用しています(詳細はこのGitHubのコメントを参照)。

セカンダリは WebSocket 接続を通じてプライマリにバックアップ実行を指示し、プライマリは以下の処理を行います:

  1. 暗号化されたメールボックスに接続。
  2. 書き込みロックを取得。
  3. wal_checkpoint(PASSIVE) によるWALチェックポイントを実行。
  4. SQLiteの VACUUM INTO コマンドを実行。
  5. コピーされたファイルが暗号化パスワードで開けることを確認(安全対策)。
  6. Cloudflare R2(または指定された場合は独自のプロバイダー)にアップロードして保存。

メールボックスは暗号化されていることを忘れないでください。WebSocket通信にはIP制限やその他の認証手段を設けていますが、不正な行為者がいた場合でも、WebSocketのペイロードにIMAPパスワードが含まれていなければ、データベースを開くことはできませんのでご安心ください。

現時点ではメールボックスごとにバックアップは1つだけ保存されていますが、将来的にはポイントインタイムリカバリー("PITR")を提供する可能性があります。

当社のIMAPサーバーは複雑なクエリや正規表現などを用いた SEARCH コマンドをサポートしています。

高速な検索性能は FTS5sqlite-regex によるものです。

SQLiteのメールボックス内では Date 値を ISO 8601 形式の文字列として Date.prototype.toISOString(UTCタイムゾーンで、等価比較が正しく機能するように)を使って保存しています。

検索クエリに含まれるすべてのプロパティに対してインデックスも保存されています。

プロジェクト

以下は当社のソースコードおよび開発プロセスで使用しているプロジェクトの一覧表です(アルファベット順):

プロジェクト 目的
Ansible サーバー群の保守、スケーリング、管理を容易にするDevOps自動化プラットフォーム。
Bree cron、日付、ms、later、ユーザーフレンドリーなサポートを備えたNode.jsおよびJavaScript用ジョブスケジューラ。
Cabin セキュリティとプライバシーを考慮した開発者向けJavaScriptおよびNode.jsロギングライブラリ。
Lad MVCなどを備えた当社のアーキテクチャとエンジニアリング設計を支えるNode.jsフレームワーク。
MongoDB メールボックス以外のすべてのデータ(アカウント、設定、ドメイン、エイリアス設定など)を保存するためのNoSQLデータベースソリューション。
Mongoose 当社のスタック全体で使用しているMongoDBのオブジェクトドキュメントモデリング(ODM)。SQLiteでもMongooseを使い続けられるように特別なヘルパーを作成しました 🎉
Node.js 当社のすべてのサーバープロセスを実行するオープンソースのクロスプラットフォームJavaScriptランタイム環境。
Nodemailer メール送信や接続作成などのためのNode.jsパッケージ。当社はこのプロジェクトの公式スポンサーです。
Redis キャッシュ、パブリッシュ/サブスクライブチャネル、DNS over HTTPSリクエスト用のインメモリデータベース。
SQLite3MultipleCiphers SQLiteの暗号化拡張機能で、データベースファイル全体(書き込み先行ログ("WAL")、ジャーナル、ロールバックなどを含む)を暗号化可能にします。
SQLiteStudio 開発用メールボックスのテスト、ダウンロード、閲覧に使えるビジュアルSQLiteエディタ(ご自身でも使用可能)。
SQLite スケーラブルで自己完結型、高速かつ堅牢なIMAPストレージのための組み込みデータベース層。
Spam Scanner Node.jsベースのアンチスパム、メールフィルタリング、フィッシング防止ツール(Spam Assassinrspamdの代替)。
Tangerine Node.jsでのDNS over HTTPSリクエストとRedisを使ったキャッシュにより、グローバルな一貫性などを実現。
Thunderbird 当社開発チームが使用し、Forward Emailと共に使う推奨メールクライアント。
UTM 当社開発チームがiOSおよびmacOSで仮想マシンを作成し、IMAPおよびSMTPサーバーと並行して異なるメールクライアントをテストするために使用。
Ubuntu 当社のインフラ全体を支えるモダンなオープンソースLinuxベースのサーバーOS。
WildDuck IMAPサーバーライブラリ。添付ファイルの重複排除に関するノートはこちら、IMAPプロトコルサポートはこちらを参照。
better-sqlite3-multiple-ciphers Node.jsでSQLite3をプログラム的に操作するための高速かつシンプルなAPIライブラリ。
email-templates 開発者向けのメールフレームワークで、カスタムメール(アカウント通知など)の作成、プレビュー、送信を支援。
json-sql-enhanced Mongoスタイルの構文を使ったSQLクエリビルダー。スタック全体でMongoスタイルの記述を続けられ、データベースに依存しないアプローチを可能にします。クエリパラメータを使うことでSQLインジェクション攻撃も防止します。
knex-schema-inspector 既存のデータベーススキーマ情報を抽出するSQLユーティリティ。すべてのインデックス、テーブル、カラム、制約などが正しく1:1であることを簡単に検証できます。スキーマ変更時には新しいカラムやインデックスを追加する自動ヘルパーも作成しており、詳細なエラー通知も備えています。
knex データベースマイグレーションとknex-schema-inspectorによるスキーマ検証にのみ使用するSQLクエリビルダー。
mandarin Markdown対応の自動i18nフレーズ翻訳ツールで、Google Cloud Translation APIを利用。
mx-connect MXサーバーの解決と接続確立、エラー処理を行うNode.jsパッケージ。
pm2 組み込みロードバランサーを備えたNode.jsのプロダクションプロセスマネージャー(パフォーマンス向けに微調整済み)。
smtp-server SMTPサーバーライブラリ。当社のメール交換("MX")および送信SMTPサーバーに使用。
ImapTest IMAPサーバーのベンチマークおよびRFC仕様のIMAPプロトコル互換性テストに役立つツール。このプロジェクトはDovecotチームによって作成されました(2002年7月からの活発なオープンソースIMAPおよびPOP3サーバー)。当社のIMAPサーバーはこのツールで徹底的にテストしています。

他に使用しているプロジェクトはGitHubのソースコードでご覧いただけます。

プロバイダー

プロバイダー 用途
Cloudflare DNSプロバイダー、ヘルスチェック、ロードバランサー、およびCloudflare R2を使用したバックアップストレージ。
GitHub ソースコードホスティング、CI/CD、およびプロジェクト管理。
Digital Ocean 専用サーバーホスティングおよびマネージドデータベース。
Vultr 専用サーバーホスティング。
DataPacket 専用サーバーホスティング。

考え方

原則

Forward Emailは以下の原則に基づいて設計されています:

  1. 常に開発者に優しく、セキュリティとプライバシーに重点を置き、透明性を保つこと。
  2. MVCUnixKISSDRYYAGNITwelve Factorオッカムの剃刀、およびドッグフーディングを遵守すること。
  3. スクラップでブートストラップされた、ラーメン収益化している開発者をターゲットにすること。

実験

要約; 最終的にS3互換のオブジェクトストレージおよび/またはVirtual Tablesの使用は、パフォーマンス上の理由で技術的に実現不可能であり、メモリ制限によるエラーが発生しやすいことがわかりました。

上記で説明した最終的なSQLiteソリューションに至るまでにいくつかの実験を行いました。

その一つは、rcloneとSQLiteをS3互換のストレージレイヤーと組み合わせて使用する試みでした。

この実験により、rclone、SQLite、およびVFSの使用に関するエッジケースをさらに理解し発見しました:

  • rcloneで--vfs-cache-mode writesフラグを有効にすると、読み取りは問題ありませんが、書き込みはキャッシュされます。
    • 複数のIMAPサーバーがグローバルに分散している場合、単一の書き込み者と複数のリスナー(例:pub/sub方式)がない限り、キャッシュはそれらの間で無効になります。
    • これは非常に複雑であり、このような追加の複雑さは単一障害点を増やす結果になります。
    • S3互換のストレージプロバイダーは部分的なファイル変更をサポートしていません。つまり、.sqliteファイルの変更はデータベース全体の変更と再アップロードを意味します。
    • rsyncのような他のソリューションもありますが、書き込み先読みログ("WAL")のサポートに焦点を当てていません。そのためLitestreamを検討しました。幸いにも、当社の暗号化はすでにWALファイルを暗号化しているため、Litestreamに依存する必要はありません。ただし、Litestreamの本番利用にはまだ自信がなく、以下にいくつかの注意点があります。
    • --vfs-cache-mode writesオプション(rclone経由でSQLiteを書き込み可能にする唯一の方法)を使用すると、データベース全体をメモリ上で最初からコピーしようとします。10GBのメールボックス1つなら問題ありませんが、非常に大容量の複数メールボックスを扱うとIMAPサーバーがメモリ制限やENOMEMエラー、セグメンテーションフォルト、データ破損に直面します。
  • SQLiteのVirtual Tables(例:s3dbの使用)を使ってデータをS3互換ストレージ上に置こうとすると、さらに多くの問題が発生します:
    • 読み書きが非常に遅くなります。S3 APIエンドポイントにHTTPのGETPUTHEADPOSTメソッドでアクセスする必要があるためです。
    • 開発テストでは、ファイバーインターネット環境で50万〜100万件以上のレコードを超えると、S3互換プロバイダーへの読み書きスループットがボトルネックになります。例えば、開発者が連続したSQLのINSERT文や大量データのバルク書き込みをforループで実行しましたが、いずれも非常に遅いパフォーマンスでした。
    • Virtual TablesはインデックスALTER TABLE文、およびその他制限があり、データ量に応じて1〜2分以上の遅延が発生します。
    • オブジェクトは暗号化されず、ネイティブの暗号化サポートもありません。
  • sqlite-s3vfsも検討しましたが、前述の問題と同様の技術的・概念的課題があります。カスタムsqlite3ビルドを暗号化ラップする(例:wxSQLite3、当社が上記ソリューションで使用中)方法もありますが、セットアップファイルの編集が必要です。
  • もう一つの可能なアプローチはmultiplex extensionの使用ですが、32GBの制限があり、複雑なビルドと開発上の問題が伴います。
  • ALTER TABLE文は必須です(したがってVirtual Tablesの使用は完全に除外されます)。knex-schema-inspectorとのフックが正しく動作するために必要であり、これによりデータ破損を防ぎ、取得した行をmongooseスキーマ定義(制約、変数型、任意のデータ検証を含む)に従った有効なドキュメントに変換できます。
  • SQLiteに関連するS3互換プロジェクトのほとんどはPythonで書かれており、当社のスタックの100%を占めるJavaScriptではありません。
  • sqlite-zstdのような圧縮ライブラリ(コメント参照)は有望ですが、まだ本番利用には準備ができていない可能性があります。代わりに、StringObjectMapArraySetBufferなどのデータ型に対するアプリケーション側の圧縮がよりクリーンで簡単なアプローチです(移行も容易で、Booleanフラグやカラム、あるいはPRAGMAuser_version=1(圧縮あり)とuser_version=0(圧縮なし)をデータベースメタデータとして使えます)。
    • 幸いにも、IMAPサーバーストレージには添付ファイルの重複排除が実装されており、同じ添付ファイルを持つメッセージは添付ファイルのコピーを保持せず、単一の添付ファイルを複数のメッセージやスレッドで共有し、外部参照を使用しています。
  • SQLiteのレプリケーションおよびバックアップソリューションであるLitestreamは非常に有望であり、将来的に使用する可能性が高いです。
    • 作者を軽視する意図はありません。彼らのオープンソースへの10年以上にわたる貢献を尊敬していますが、実際の使用では多くの問題データ損失の可能性が報告されています。
  • バックアップの復元は摩擦なく簡単である必要があります。MongoDBのmongodumpmongoexportのようなソリューションは面倒で時間がかかり、設定も複雑です。
    • SQLiteデータベースは単一ファイルなので簡単です。
    • ユーザーがいつでも自分のメールボックスを持ち出せる設計にしたかった。
      • シンプルなNode.jsコマンドでfs.unlink('mailbox.sqlite')を実行すれば、ディスクストレージから完全に削除されます。
      • 同様にS3互換APIのHTTP DELETEを使って、ユーザーのスナップショットやバックアップを簡単に削除できます。
    • SQLiteは最もシンプルで高速かつコスト効率の良いソリューションでした。

代替手段の欠如

私たちの知る限り、他のメールサービスでこのように設計されているものはなく、オープンソースでもありません。

これは既存のメールサービスがスパゲッティコード 🍝 を含むレガシー技術を本番環境で使用しているためだと考えられます

既存のメールサービスプロバイダーのほとんどはクローズドソースであるか、オープンソースとして宣伝していますが、実際にはフロントエンドのみがオープンソースです。

メールの最も重要な部分(実際のストレージ/IMAP/SMTPのやり取り)はすべてバックエンド(サーバー)で行われており、フロントエンド(クライアント)ではありません

Forward Emailを試してみる

今すぐ https://forwardemail.net に登録しましょう! 🚀