Ethereumの秘密鍵はどこに保存するべきかを考える


はじめに

既にEthereumに慣れ親しんだ人にとってはあたりまえの話かもしれませんが、
これから始める人にとっては、秘密鍵はどこに置いたらいいのか結構悩むポイントだと思います。
EthereumだけでなくBlockchainにとって、秘密鍵はとてもとても大事なものなので、
絶対に無くさないようにしたいのですが、使いたいときに使えないのでは意味がないのですよね。

秘密鍵を置く場所によってシステムやアプリの構成やセキュリティ要件が変わってくるので、
今回はEthereumを使ったアプリを作るときに、どこに秘密鍵を保存するべきかについて考えていこうと思います。
仮想通貨とアプリでは保存する場所の観点が違うので、今回は アプリ の場合に限定します。

自己紹介

NTTテクノクロスの深津颯騎です。
普段はBlockchainを使ったアプリ開発や、BCのインフラをどう構成するかを研究したり、
ContractGateシリーズの開発をしています。近いうちになんか出します。

保存場所はどこがある?

まずはじめに、秘密鍵を置く場所ってどこがあると思いますか?
パッと思いつくだけでもこれくらいあると思います。

それぞれの配置場所について考える

まずは管理方式について考えてみましょう。
どちらの方法を選んだとしてもリスクは必ずあるので、
ご自身でメリットデメリットを天秤にかけてから、納得できる方法を選んでください。

自己管理方式

自分の秘密鍵は自分で管理するスタイルですね。
大事な秘密鍵なので、なるべくなら自分の手の届くところで管理したいですよね。
この場合は、保存場所に対するセキュリティ(不正アクセス対策)は自分でなんとかする必要があるのが課題です。
ブロックチェーンの世界において秘密鍵があればなんでもできてしまうので、なるべく 秘密鍵は自分の手元に置いておく ことをお勧めします。

自己管理方式では、秘密鍵がすぐに使える状態にある「ホットウォレット※7」と、使うまでに時間がかかる「コールドウォレット※8」があります。
Ethereumに対する操作をするときに、秘密鍵を使った署名を行わなければいけないので、署名の頻度が高い場合(アプリを使うときなど)は「ホットウォレット」を使い、
逆に、署名の頻度が少なく、仮想通貨やトークンを長期間保管することが大事な場合は、「コールドウォレット」を使いましょう。

ホットウォレット、コールドウォレットともにもちろんリスクはありますので、自分のユースケースと比較して、合っている方を使いましょう

リスク

  • ホットウォレットの場合は、秘密鍵にアクセスするまでの経路が、セキュリティ的に脆弱であった場合は流出する可能性があります。
  • コールドウォレットの場合は、オフライン上の物理的な媒体に秘密鍵を保存するので媒体自体を紛失する可能性や、 突発的なアクシデントによってアクセス方法が消失してしまう可能性があります※3,4。

第三者管理方式

このパターンでは、秘密鍵を信頼できる第三者に委託する方式となっています。
第三者が信頼できる場合においては、自分でセキュリティ対策を講じるよりも強固に守ってもらえる可能性が高いです。
IT詳しくない人にとってはセキュリティを強固にしてくださいと言われても、ピンとこない方も多いんじゃないでしょうか。
また、クラウド型ウォレットサービスの場合は、どの端末からアクセスしても、同じ秘密鍵を使えることもメリットとしてあると思います。

しかし、委託先に秘密鍵があると、それはそれでリスクを抱えることになります。
委託先が不正アクセスを受けたり※1、内部犯行等※2で秘密鍵を流出してしまう可能性は考慮しておきましょう。

この方式を採用する場合は、 第三者が信頼できるか の見極めが大事です。

アンチパターン

アプリで使うときの秘密鍵をどこに保存するべきかが今回のテーマなので、
あたらめてこのテーマで使える保存場所を整理してみましょう。
コールドウォレットはアプリがすぐに使えないので除外します。
紙に書いて金庫で保管とか、セキュリティが原点回帰してる気がして個人的には好きなんですけどねー。

まずはじめに、残ったパターンから、NTTテクノクロスがEthereumを使ったアプリを作るときに、
NG としている構成について解説していきます。

Go-Ethereum(Geth)内に鍵を保管することはNG

自己管理方式、第三者管理方式ともに、これは絶対にやらないことにしました。
なぜかというと、不正アクセスのリスクが高すぎるためです。

自己管理方式の場合

まずは自己管理方式の場合から考えてみましょう。
Ethereumのアプリを使うときに、自分のGethを経由することになります。
一見よさそうに思えるかもしれませんが、いくつか罠があるので見ていきましょう。

罠1: 適当なアクセス制限設定が危ない

不用意にどこからでもアクセスを許可していたり、公開する必要のないAPIを公開しているとひどい目にあいます。
悪い人はいつでもGethのポートが開いていないか狙っているのです。※5,6
Gethの場合、悪い人がこっそり繋いでいても検知する方法がありません。
TCPレイヤを監視していればわかりますけど、Gethには接続時の認証機能がないので、接続してきている人が正規ユーザーなのか不正なのかを見分けられません。
アクセス制限はしっかりと設定しましょう。

以下の起動オプションを適切に設定して、Gethでアクセス制限をしっかりかけましょう
GlobalIPを割り当てたGethで、うっかり--rpcaddr 0.0.0.0/0 --rpccorsdomain "*"で全公開してると悲しい目にあいます...。

--rpcaddr value                     HTTP-RPC server listening interface (default: "localhost")
--rpcport value                     HTTP-RPC server listening port (default: 8545)
--rpccorsdomain value               Comma separated list of domains from which to accept cross origin requests (browser enforced)
--rpcvhosts value                   Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost")
--wsaddr value                      WS-RPC server listening interface (default: "localhost")
--wsport value                      WS-RPC server listening port (default: 8546)
--wsorigins value                   Origins from which to accept websockets requests

罠2: 秘密鍵を使うときのパスワード認証方式が危ない

Gethの場合、秘密鍵を使って署名をするときには、パスワードによる認証(アンロック)が必要になります。
普段はロックがかかっている状態なので、秘密鍵を使って知らない人が勝手に悪いことされないようになっています。
ロックを解除(アンロック)したときだけ、秘密鍵を使える状態になります。

この パスワード認証をどこでするのかが問題 なのです。

  • パターン1: Geth起動時にアンロックしておく。

    一応Gethの起動パラメータにはアンロックしておくためのオプションがあります。が、可能な限り使わないようにしましょう。
    いつでもだれでもGethにアクセスさえできれば秘密鍵が使い放題になってしまうので、
    万が一不正アクセスがあった場合に秘密鍵を守れません。
    悪い人はいつでもGethのポートが開いていないか狙っているのです。※5,6
    開いていたら即Ethの残高を0にされるでしょう。

  • パターン2: トランザクション作成時だけアンロックする。

    なら使うときだけアンロックすればいいじゃんと思ったあなた!。甘いです。甘すぎます。
    何度も繰り返し言いますが、悪い人は知らない間にあなたのGethに繋いでいる可能性があるのです。※5,6
    秘密鍵のアンロックが一瞬だとしても、その一瞬のうちにやられるでしょう。
    また、アンロックのコマンドを外部(webアプリ)から行うことを許可するためには、Gethに怪しい起動コマンドを付ける必要があります。

    オプションのヘルプにも「安全でないアカウントのアンロックを許可する(Allow insecure account unlocking)」って書かれてますよね。
    このようにGethの開発者も、なるべくなら使ってほしくないようです。

  $ geth -h | grep unlock
  --allow-insecure-unlock             Allow insecure account unlocking when account-related RPCs are exposed by http

第三者管理方式の場合

この方式の場合は、不特定多数が同じGethにアクセスする前提となります。
そのため、「自己管理方式」でお伝えしたアクセス制限を掛けることは、
利用者がどこからどのようにして繋いでくるのかわからないので難しいでしょう。
それ以外にも罠がありますので見ていきましょう。

罠3: ブルートフォースアタックに対して脆弱

悪い人がGethにアクセスできたと仮定したときに、秘密鍵のパスワード解読を試みると思います。
もちろんアンロックされるまで待つケースもありますけど、いつになるかわかりませんからね。
通常のパスワード認証のシステムであれば、リトライ回数をかけたり、失敗したらアカウントをロックする運用をとっていると思います。
Gethの場合はこの機構がないので、何回もリトライし放題です。
あとは...言いたいことはわかりますよね...?

Gethの機能では防げないので、ここよりも外側で対策する必要があるのです。
サービス提供者がこのあたりのセキュリティ対策をしていることを確認しましょう。

罠4: 内部犯行

内部犯行※2は防ぐことができません。あきらめてください。
複数人の鍵が同じところに保管されていることになるので、管理者に悪意があった場合は大変なことになるでしょう。

罠5: 紛失のリスク

秘密鍵はデータなので、何らかの拍子にデータが紛失する可能性が0に近いですがありえます。
たとえばサーバのメンテナンスに失敗したとか、冗長構成のHDDが一気に複数台壊れてRaidが復旧できなくなったとか...。
念のため自分の手元でも秘密鍵のバックアップをしておきましょう。

推奨パターン

秘密鍵は利用者自身の手元にあり、Ethereumにリクエストを送るときだけ署名する方式を推奨しています。
なるべくなら秘密鍵はGethには置かず、アプリを使うシステム側に配置しましょう。
秘密鍵で正しく署名された電文があれば、どのGethにも送ることができるので、GethはEthereum Networkに対するGateway的な使い方をオススメします。
特定のGethが故障したとしても、他のGethに宛先を切り替えれば継続して動きますからね。

秘密鍵はシステム内部に組み込んだり、ウォレットアプリを活用することで、Gethに対する不正アクセスのリスクを回避しましょう。
当然のことですが、システムやウォレットの不正アクセス対策はしっかりとやることは必要です。


mnemonicを使った秘密鍵の作り方

ついでなので、秘密鍵を生成する方法も一工夫いれましょうか。
鍵の紛失リスクを抑えるために、mnemonicを使った秘密鍵の生成ロジックがあります。
元々はBitcoinのBIP39,44と呼ばれる規格でしたが、Ethereumにも活用されています。
mnemonicと呼ばれる複数の単語からなるワードリストと階層をメモしておくことで、秘密鍵を復元することができるようになっています。
秘密鍵のバックアップにも使えるので、物理的な紙にメモして金庫にしまっておきましょう。
詳しい内容はSlideshareにまとめてくれている方がいるので参照してください※9

もっとセキュアにしたいなら

ハードウェアセキュリティモジュール(HSM)を使うという方法もあります。
これなら秘密鍵自体を箱の中から出さないで済みますね。
セキュリティ向上に効果的ですが、お値段も結構します。(1台いくらするんだろう...)

まとめ

  • 秘密鍵はGethに置かない
  • アクセス制限はしっかりとやる
  • システム内で秘密鍵を使って電子署名を付ける
  • GethはBlockchain NetworkへのGateway的に使う
  • 不正アクセスこわい

脚注

※1: 仮想通貨NEMの不正送金に関するご報告と対応について
※2: ビットステーション株式会社に対する行政処分について
※3: Message from QuadrigaCX
※4: およそ160億円分の仮想通貨が全喪失の可能性、取引所CEOの死去によってウォレットへアクセスできなくなったため
※5: 平成30年におけるサイバー空間をめぐる脅威の情勢等について
※6: EthereumのJSON RPCにおけるスキャン活動の観測
※7,8: 正確には、オンラインを含むすぐにBlockchain Networkに接続できるものを「ホットウォレット」。オフラインで保管されているものが「コールドウォレット」です。
※9: 階層的決定性ウォレットを理解しよう