サーバーサイド鍵管理の最適解を探る
サーバーサイドでウォレットを管理する
基本的には認可がなければ顧客の暗号資産管理はできないため、あくまで運営側のウォレットを管理することになります。
いくつか意思決定のポイントがあると思います。
- 書き込みトランザクションを必要とするか
- 少数でよいか、それとも大量にウォレットが必要か
- ホットな環境で書き込みトランザクションを実行するか
事前に要点だけ記しておくと、
- 秘密鍵を扱いたくないので書き込みする場合はなるべくKMSを利用したい
- ただし、KMSはHDウォレットのルートにはできない
- コスト的に大量生成には向かない
- HDウォレットで書き込みを避ける場合
- ハード化する場合は、コールド環境で事前のバッチ生成が必要
- ハード化しない場合は、公開鍵のみで子鍵生成できるが子の秘密鍵が漏洩した際の被害が大きい
書き込みトランザクションを必要とするか
一切の書き込み操作が不要なケースでは、秘密鍵を捨ててしまってよくなります。
たとえば、ユーザーに入庫先としてアドレスを提供し、入庫内容の確認ができれば済むなどの要件では書き込みトランザクションは不要になります。
そのため、Wallet.createRandom()
で生成した公開アドレスのみをDBなどに記録し、そのアドレスをユーザーに提供するだけでよいです。運営側ですら秘密鍵は捨ててしまいます。
ただし、ランダム生成の場合は、ユーザーごとに決定的なアドレスにはならないため、必要に応じてHDウォレットを利用して決定的なアドレスを生成するとよいでしょう。
HDウォレットを利用する場合は、また別の意思決定が必要になるため後述します。
少数でよいか、それとも大量にウォレットが必要か
ごく少数のウォレットがあれば良いケースでは、クラウドが提供するKMSを利用してそもそも秘密鍵を持たずに署名機能のみ得るという手法が使えます。
KMSはキーあたり月$1程度が相場のため、ユーザー全員にKMSを割り当てるという手法は経済的に成り立たないケースが多いと思われます。
そのため、基本的には少数のウォレットを管理する場合のみ利用することになります。
一方で、大量にウォレットが必要な場合、たとえばユーザーごとの入庫先が必要といったケースでは、HDウォレットを利用して、1つの秘密鍵から決定的に大量のウォレットを生成する手法が必要になります。
この際、HDウォレットは秘密鍵それ自体が必要という点が重要です。
つまり、KMSのように署名能力のみを提供する形式のウォレットをHDウォレットにすることはできません。
さらに、HDウォレットを使用する際は、ユーザーごとの鍵生成でハード化を行うかどうかも重要です。
こちらの記事で詳細は述べていますが、ハード化しない場合は、子の秘密鍵が漏洩すると親の秘密鍵まで漏洩する危険があります。
一方で、ハード化しないことで親の公開鍵のみを使って、子の公開鍵を生成できるメリットもあるため、そのトレードオフを理解しておくことが重要です。
ホットな環境で書き込みトランザクションを実行するか
ホットな環境で書き込みトランザクションを実行する場合は、秘密鍵または署名能力をホットな環境に展開する必要があります。
たとえば、ユーザーの操作に応じてサーバーで自動で入庫用アドレス内の資産を操作することが必要な場合などがそうです。
- 入庫された暗号資産を転送、あるいは消費する
- 入庫されたNFTをtransfer、あるいはburnする
ユーザーごとに入庫先アドレスを提示したうえで、そのアドレスの秘密鍵を用いてトランザクションを発行するようなケースでは、ハード化したHDウォレットを用いることになるでしょう。
子の秘密鍵をサーバーのようなホット環境で取り扱う以上、漏洩の危険性は高くなります。
上述した通り、ハード化しない場合は親の秘密鍵漏洩にまで被害が拡大する危険があります。
ハード化したHDウォレットを用いることで、子の秘密鍵が漏洩してもそのアドレス以下のみの被害にとどめられるようにすることが重要です。
一方で、完全に入庫のみで書き込みを行わず、入庫されるものに資産性が低いようなケースでは、ハード化しないHDウォレットを用いることも判断としてありうるかもしれません。
親の公開鍵のみで子の公開鍵を生成できるため、事前にコールド環境で親の公開鍵生成さえしてしまえば、サーバーサイドで秘密鍵を一切扱う必要がなくなります。
HDの子ウォレットに対しては、なるべく書き込み不要な要件とする
基本的に、HDウォレットに対する書き込みトランザクションをサーバーで行おうとすると、なんらかの秘密鍵の平文がメモリ上であれサーバーサイドに展開されることになります。
そのため、なるべくこのようなことが発生しない要件でシステムを構築できることが望ましいでしょう。
次点の落とし所としては、基本は書き込み不要だが、緊急時はコールド環境で書き込みを行うなどが考えられます。
このケースでは、ハード化した子の公開鍵をユーザーに提供するためにコールド環境での事前のバッチ生成が必要になります。
まとめ
- 一切の書き込みが不要:ランダム or 非ハード化HDウォレットの簡易手段も検討可
- 少数ウォレットで書き込みあり:KMSを利用し秘密鍵管理を回避
- 大量ウォレット:ハード化HDウォレットを事前にバッチ生成し、書き込みはコールド環境で行う
あくまで上記の選択肢は一例です。
要件によっては、たとえば別の切り口でAA(Account Abstraction)を利用することでエレガントに解決が可能なケースもあるかもしれません。
また、ある程度の非同期が許されるケースでは、マルチシグによる承認ゲートを伴うような実装も検討できます。
構築するシステムの要件とトレードオフのもとに十分に検討して、適切なウォレット設計を行うことが重要です。
Discussion