Cloudflare WorkersとWeb Crypto APIで構築する「ゼロ知識暗号化」メール基盤の実装詳細
Email WorkersでのMIMEストリーム受信、エッジ上でのエンベロープ暗号化、そしてD1データベースへの安全な隔離保存の全貌
プライバシー重視の使い捨てメールサービス「KeepTempMailer」を設計するにあたり、私たちが最初に掲げた絶対条件は**「サービスの管理者であっても、ユーザー宛てに届いた確認コードやログインリンクを一切読めないこと」**でした。
従来のメールサーバー(PostfixやDovecotなど)を用いた構成では、メールスプールディスクに平文が書き込まれる瞬間が必ず存在し、root権限を持つインフラ運用者がデータを覗き見ることが可能でした。
私たちはこの問題を解決するために、**Cloudflare Workers(Email Routing + Email Workers)**と**W3C標準 Web Crypto API**を組み合わせ、エッジサーバーの揮発性メモリ上で受信した瞬間に暗号化し、データベースには不可逆な暗号バイナリしか残さない「真のゼロ知識メールパイプライン」を構築しました。本記事では、その具体的な実装コードとアーキテクチャの裏側を完全公開します。
1. なぜメール受信基盤にCloudflare Workersを採用したのか?
メールサーバーを自前で立てる場合、24時間365日の死活監視、スパムボットからのDDoS攻撃対策、TLS証明書の更新など、膨大な運用コストがかかります。
Cloudflare Email Workersを採用した主な理由は以下の3点です。
- ステートレスな実行環境: V8アイソレート上で動作するため、メール処理が完了した瞬間にプロセスとメモリが完全に破棄され、サーバー上に平文メールの痕跡が一切残りません。
- 世界最速のエッジ暗号化: 世界300都市以上のデータセンターで受信処理が行われるため、わずか数ミリ秒で暗号化が完了します。
- 暗号APIの標準準拠: Workers環境にはブラウザと同じ
crypto.subtle(Web Crypto API)が組み込まれており、クライアントサイドと完全に同一の暗号プリミティブで検証・テストが可能です。
2. ゼロ知識メール受信のアーキテクチャ全体像
メールが届いてからユーザーの手元で復号されるまでのステップは、以下のように厳密に分離されています。
- 送信元サーバーからCloudflare Email RoutingへSMTP配送(TLS暗号化)
- Email Workerがトリガーされ、生メールストリーム(MIME形式)を受信
- 受信者アドレスに紐づく「ユーザーのRSA公開鍵」をCloudflare D1から取得
- Workers内で使い捨てのAES-256-GCM共通鍵を生成し、メール本文を暗号化
- AES共通鍵をユーザーのRSA公開鍵でカプセル化(RSA-OAEP)
- 暗号化パッケージ(暗号化本文 + 暗号化鍵 + IV)をD1へ保存し、メモリを破棄
- ユーザーがPWAアプリを開くと、端末内の秘密鍵でブラウザ上でのみ復号
3. Workers上のEmailハンドラ:平文ストリームの即時暗号化コード
以下は、本サービスの Workers バックエンドで実際に動作しているメール受信ハンドラのコア実装です(抜粋)。
// Cloudflare Email Worker エントリポイント
export default {
async email(message, env, ctx) {
const recipient = message.to;
// 1. D1から受信者のRSA公開鍵(SPKI形式)を取得
const userKeyRecord = await env.DB.prepare(
"SELECT public_key_spki FROM email_accounts WHERE address = ?"
).bind(recipient).first();
if (!userKeyRecord) {
// 存在しないアドレス、またはキルスイッチで無効化されたアドレスは即座にドロップ
message.setReject("Address not found or terminated.");
return;
}
// 2. MIMEストリームを読み込み
const rawEmail = await new Response(message.raw).text();
// 3. 使い捨てのAES-256-GCM鍵(DEK)を生成
const dek = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
// 4. メール平文をAES-GCMで暗号化
const iv = crypto.getRandomValues(new Uint8Array(12));
const encodedPayload = new TextEncoder().encode(rawEmail);
const encryptedBody = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv: iv },
dek,
encodedPayload
);
// 5. ユーザーのRSA公開鍵をインポートし、DEK自体を暗号化
const rsaPublicKey = await crypto.subtle.importKey(
"spki",
hexToBuffer(userKeyRecord.public_key_spki),
{ name: "RSA-OAEP", hash: "SHA-256" },
false,
["encrypt"]
);
const exportedDek = await crypto.subtle.exportKey("raw", dek);
const encryptedDek = await crypto.subtle.encrypt(
{ name: "RSA-OAEP" },
rsaPublicKey,
exportedDek
);
// 6. 暗号化されたバイナリのみをD1へ保存(平文はここでガベージコレクション)
await env.DB.prepare(
"INSERT INTO encrypted_emails (id, address, encrypted_dek, iv, ciphertext, created_at) VALUES (?, ?, ?, ?, ?, ?)"
).bind(
crypto.randomUUID(),
recipient,
bufferToHex(encryptedDek),
bufferToHex(iv),
bufferToHex(encryptedBody),
Date.now()
).run();
}
};
4. ハイブリッド暗号化の実装:AES-256-GCM と RSA-OAEP の連携
RSA暗号は強力ですが、計算コストが高く、暗号化できるデータサイズが鍵長(2048bitの場合は最大214バイト程度)に制限されます。メールのように数キロバイト〜数メガバイトに及ぶデータを暗号化するには、**ハイブリッド暗号方式**が必須です。
この方式を採用することで、高速な対称暗号(AES-GCM)で大容量データを瞬時に暗号化しつつ、その鍵の受け渡しには非対称暗号(RSA-OAEP)を用いることで、処理性能と安全性の両方を極限まで引き出しています。
5. Cloudflare D1 データベースへの暗号化バイナリ格納
データベースには、16進数(Hex)文字列化された encrypted_dek、iv、ciphertext のみが保存されます。
仮にCloudflareアカウントのクレデンシャルが漏洩し、D1データベースの全データが第三者にダンプされたとしても、保存されているのは「ユーザーの端末内にしかないRSA秘密鍵でしか開けない暗号文」だけです。秘密鍵は端末のIndexedDBから外部にエクスポートできない設定(extractable: false)になっているため、データ流出被害は原理的にゼロとなります。
6. まとめ:エッジコンピューティングが変えるプライバシーの未来
従来のサーバーセントリックなWebアプリケーションでは、「プライバシーを守る」という約束は、企業のセキュリティポリシーや善意に依存していました。
しかし、Cloudflare Workersのようなモダンなエッジコンピューティングと、ブラウザ標準のWeb Crypto APIを正しく組み合わせることで、**「技術的・数学的に平文を読めないシステム(ゼロ知識証明・暗号化)」**を個人開発の規模でも堅牢に実装できる時代が到来しました。
KeepTempMailerは、今後もオープンで検証可能なプライバシーアーキテクチャの普及に貢献してまいります。
Cloudflare Workers・WebCrypto API・サーバーレス暗号インフラ設計
「運営者すら読めない」ゼロ知識使い捨てメール管理システムのコアアーキテクチャを設計・開発。 最新のエッジコンピューティングと暗号学を活用し、誰でも手軽にプライバシーを防衛できるツールの開発を行っています。