Sparrow Wallet v2.4.2: TOFU証明書ピンニングによるサーバー接続の信頼モデル強化
Sparrow Wallet v2.4.2では、Electrumサーバーおよび Bitcoin Core RPC接続に対する証明書検証ロジックが大幅に見直されました。公開サーバーへの接続では証明書タイプに応じてCA検証とTOFUピンニングを自動的に切り替え、Bitcoin Core へのTLS接続にはTOFU証明書ピンニングが新たに実装されています。
なぜウォレットの接続先認証が重要なのか
Bitcoinウォレットがサーバーに接続する際、通信路は当然TLSで暗号化されるべきですが、暗号化だけでは不十分です。暗号化は通信内容の秘匿を保証しますが、接続先が正当なサーバーであることは保証しません。
攻撃者がDNSスプーフィングやネットワーク経路のハイジャックを行い、偽のElectrumサーバーやBitcoin Coreノードに接続させることができれば、以下のような攻撃が可能になります。
- 偽の残高表示: 実際には受け取っていないBTCを受領済みと表示させ、商品やサービスの詐取に利用する
- トランザクション傍受: ブロードキャストされたトランザクションを傍受し、アドレスやUTXOの情報を収集する
- 手数料推定の操作: 不正に高い(または低い)手数料レートを返し、ユーザーに経済的損失を与える
- 二重支払い攻撃の隠蔽: 確認済みトランザクションの情報を偽装し、ゼロ確認取引を安全に見せる
このため、接続先の認証(Authentication)は暗号化と同等以上に重要です。
2つの認証モデル: CA検証とTOFU
TLS接続における認証には、大きく分けて2つのモデルがあります。
CA(認証局)検証
インターネットの標準的なTLS認証モデルです。サーバーの証明書がLet's EncryptやDigiCertなどの信頼された認証局(CA)によって署名されているかを検証します。クライアント側にはOS/JVMにプリインストールされたCA証明書のリスト(トラストストア)があり、チェーンを辿って検証が行われます。
[サーバー証明書] → [中間CA証明書] → [ルートCA証明書(トラストストアに存在)]
利点は初回接続から信頼性が確保されることです。欠点はCA自体が侵害された場合や、政府機関によるCA強制を受けた場合に脆弱であることです。
TOFU(Trust On First Use)ピンニング
SSHのknown_hostsと同じモデルです。初回接続時にサーバーの証明書(または公開鍵)のフィンガープリントを記録し、以降の接続では記録と一致するかを検証します。
初回接続: サーバー証明書を記録 → ローカルに保存
2回目以降: サーバー証明書 == 保存済み証明書 → OK
サーバー証明書 != 保存済み証明書 → 警告/接続拒否
利点はCAインフラへの依存がないことです。自己署名証明書でも安全に使えます。欠点は初回接続時にMITMが行われると、攻撃者の証明書を信頼してしまうリスクがあることです(ただし、その後の攻撃者交代は検知可能です)。
なお、Sparrowには以前からSSL証明書ピンニングの仕組みが存在していました。v1.3.2で@lukechildsによりSSLサーバー証明書ピンニングが追加されています。v2.4.2の変更は、この仕組みをCA検証とTOFUの使い分けに発展させたものです。
Electrumプロトコルにおける証明書検証の課題
Electrumプロトコルはサーバーとの通信にTLS(SSL)を使用しますが、サーバーの証明書は運用者によって大きく異なります。
- 公開サーバー: Let's Encryptなどで取得したCA署名証明書を使用するケースが多い
- プライベートサーバー: 自宅やVPS上で運用し、自己署名証明書を使用するケースが一般的
- Tor Hidden Service経由: .onionアドレス自体が暗号学的な認証を提供するため、証明書検証の重要性は相対的に低い
問題は、CA署名証明書と自己署名証明書を同じロジックで検証できないことです。CA署名証明書に対してTOFUピンニングを行うと、証明書の正当な更新(Let's Encryptの90日ローテーションなど)のたびに接続が切れてしまいます。逆に、自己署名証明書に対してCA検証を行うと、常にエラーになります。
Electrumクライアント(Python版)はこの問題に対して、初回接続時に証明書タイプを記録する方式を採用していました。Sparrow v2.4.2はこのアプローチを明確に実装しています。
Sparrow v2.4.2の実装: 証明書タイプに基づく自動切り替え
新しいロジックの概念を擬似コードで示します。
// 接続時の証明書検証ロジック(概念的な擬似コード)
void validateServerCertificate(X509Certificate[] chain, String host) {
CertificateRecord stored = loadPinnedCertificate(host);
if (stored == null) {
// 初回接続: 証明書タイプを判定して記録
boolean isCaSigned = verifyCaChain(chain);
storeCertificateRecord(host, new CertificateRecord(
isCaSigned ? CertType.CA_SIGNED : CertType.SELF_SIGNED,
getFingerprint(chain[0])
));
return; // 初回は信頼
}
if (stored.type == CertType.CA_SIGNED) {
// CA署名: 標準のCA検証チェーンを使用
// 証明書が更新されてもCAチェーンが有効なら問題なし
if (!verifyCaChain(chain)) {
throw new CertificateException(
"CA validation failed for " + host);
}
} else {
// 自己署名: TOFUピンニング
// フィンガープリントが一致しなければ拒否
String currentFingerprint = getFingerprint(chain[0]);
if (!currentFingerprint.equals(stored.fingerprint)) {
throw new CertificateException(
"Certificate fingerprint changed for " + host +
". Expected: " + stored.fingerprint +
", Got: " + currentFingerprint);
}
}
}
JavaのTLS実装では、この種のカスタム検証は X509TrustManager インターフェースを実装することで実現されます。以下は一般的な実装パターンの擬似コードです。
// Java TLS でのカスタム証明書検証パターン(擬似コード)
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{
new X509TrustManager() {
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
// ここにCA検証/TOFUピンニングの分岐ロジック
validateServerCertificate(chain, currentHost);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) {}
}
}, new SecureRandom());
重要な点として、Sparrowは空のTrustManagerを使って全証明書を信頼するようなことは一切しません。これはセキュリティアンチパターンとして広く知られており、Sparrowは証明書タイプに応じた適切な検証を常に行います。
Bitcoin Core RPC接続のTOFUピンニング
2つ目の変更は、Bitcoin Core RPCへのTLS接続に対するTOFUピンニングの実装です。
SparrowにはCormorantという内蔵のElectrumサーバー互換レイヤーがあり、Bitcoin CoreのJSON-RPC APIに接続してブロックチェーンデータを取得します。ユーザーがリモートのBitcoin Coreノード(自宅サーバー、VPS等)にTLS経由で接続する場合、この通信路の認証が必要になります。
Bitcoin Core自体はTLS対応のRPCを提供しますが、一般的にCA署名証明書ではなく自己署名証明書で運用されることが多いです。この場合、CA検証は機能しないため、TOFUが適切な選択となります。
[Sparrow] --TLS--> [Bitcoin Core RPC (自己署名証明書)]
|
├── 初回: 証明書フィンガープリントを ~/.sparrow/certs/ に保存
├── 2回目以降: フィンガープリントを比較
└── 不一致: 接続拒否 + 警告表示
この仕組みにより、初回接続後は攻撃者がネットワーク経路を乗っ取っても、偽のBitcoin Coreノードに接続させることができなくなります。SSHで初めてサーバーに接続した後、ホスト鍵が変わると警告が出るのと同じ原理です。
自前Electrumサーバーを運用するユーザーへの影響
プライバシーを重視するユーザーは、Fulcrum、electrs、ElectrumXなどの自前Electrumサーバーを運用し、自己署名証明書でTLSを設定していることが多いです。このケースでは、v2.4.2の変更が以下のように作用します。
1. 初回接続時: Sparrowはサーバーの証明書が自己署名であることを検出し、フィンガープリントをローカルに保存します
2. 通常の接続: 保存済みフィンガープリントと照合し、一致すれば正常に接続されます
3. 証明書の更新時: フィンガープリントが変わるため、Sparrowが警告を表示します。ユーザーは新しい証明書を確認した上で承認します
この動作により、サーバーの証明書を意図的に更新した場合は再承認が必要になりますが、意図しない証明書変更(MITM攻撃の兆候)を確実に検知できるようになります。
まとめ: 防御の層を重ねる
Sparrow v2.4.2のTLS関連の変更をまとめると、以下の4層の防御が構築されたことになります。
| 接続先 | 認証方式 | 保護対象 |
|---|---|---|
| 公開Electrumサーバー(CA証明書) | CA検証チェーン | 初回接続からのMITM |
| プライベートElectrumサーバー(自己署名) | TOFUピンニング | 2回目以降のMITM |
| Bitcoin Core RPC(TLS) | TOFUピンニング | リモートノードへの接続偽装 |
| Cormorant内部サーバー | localhostバインド | 外部からの不正アクセス |
これらは個々には小さな変更に見えますが、Bitcoin自己管理(self-custody)の文脈では、ウォレットが「正しいブロックチェーンの状態を見ているか」という根本的な信頼の問題に直結します。Sparrowがウォレットの接続レイヤーにここまで注意を払っていることは、セキュリティとプライバシーへの真剣なコミットメントの表れだと言えるでしょう。
Sparrow Wallet v2.4.2は2026年3月10日にリリースされました。ダウンロードは sparrowwallet.com/download から。





