ミカン(@cryptomikan)さん | Twitterが書いた記事のツイートに対してコメントしたので、思うところを書きました。

秘密鍵の管理方法と悪用への対策 - Qiita

以下、指摘とかアドバイスとかではなくただのポエムです。

NISとのHTTP通信

残高やトランザクションの取得

HTTPなので平文がやり取りされますし、相手も本当にNISなのかはわかりません。

なので、残高などを取得していたサーバが実はNISに似た応答を返す偽NISかもしれません。

しかし、サーバは分散しているので他のノードへの問合せ結果と比較することで正しい内容を確認することができるでしょう。

この点については、偽物が紛れ込んでいても、他のノードで検証可能という意味で脆弱性ではない、仕組み上しょうがないことのうちだと思います。

これについては、アプリケーション側(できればSDKでサポートしてほしい)で複数のノードからデータを取得して検証するなどの仕組みがあれば解決できることです。

Don’t Trust, Verifyってやつですかね。

トランザクションの発行

では送金する場合はどうでしょうか。

HTTPなのでトランザクションデータもやはり平文でインターネット上を流れます。

トランザクションデータには、どれくらいのXEMをどのアドレスへ送るかなどの情報を持ちます。

ということはそれが書き換えられたら、送るXEM量や宛先を変えられてしまう…?

なんてことにはなりません。

ここで実際にNISへ送られるデータを見てみましょう。

nis-and-https

これはNanoWalletで送金をしたときに、開発者ツールで実際にNISへ送られたリクエストボディです。(改行などは足しました)

nis-and-https

もしくはL.139148 announceメソッドにブレークポイントを仕掛ければ、serializedTransactionの値を取得できます。

(コンパイル済みのファイルなのでNanoWallet/nem-sdkのバージョンにより行は変化します)

{
    "data":"01010000010000983d6bbd0520000000f6acb5053836b8ebcea08af2086d3b4e1e88e515c124d71a54d8f1a514af09eb50c30000000000004d79bd052800000054445757594447514e424b53414a4253485a58375157565837574e56415757423748475057524232809698000000000000000000",
    "signature":"e0fc14b5a3fb12adacc6005f1f447fa9846e712935eb1ec746bbf9f3376e3e0157a1183c81b028824e3c4b115a2d2ce085d6974b57c8ac61d66b779ea1a1960f"
}

json形式で”data”と”signature”というキーでデータが送られていることがわかります。

data部は、具体的な送金の依頼をシリアライズ(ネットワーク上で扱いやすいような形式として文字列化)したデータです。

これはわかりやすい形式へデシリアライズすることができます。

トランザクションのデータを読めるように変換するツールを作りました。

ここのOffline Transactionとあるテキストエリアへコピペしてください。

nis-and-https

それぞれの情報が読めるようになりました。

ではこれを書き換えて、例えば宛先アドレスを別のものに変えて、シリアライズしなおして、data部を置き換えたら…?

NISには受理されません。

それは、このdata部が送り主の秘密鍵によって、この内容で間違いないということを署名したデータがsignature部にあり、これで検証されるからです。

署名をし直すためには署名者の秘密鍵が必要になるため、signature部を改ざんすることも不可能です。

どちらか改ざんされていれば、”FAILURE_SIGNATURE_NOT_VERIFIABLE”(署名検証不可)というエラーがでて、NISに受け付けてもらえません。

というわけで、署名済みの送金依頼は変更することが出来ず、あとはNISに送るくらいしかできません。

ちなみにNISへトランザクションデータを送るまでならインターネットにつながっていなくてもできるので、NanoWalletのオフライントランザクションは、このNISへ送る直前のデータを作るところまでをやる機能です。

生成されたQRコードまたはデータはNISに送られる直前のデータと同じものです。

nis-and-https

変更することができないので、このオフライントランザクションは不特定多数に晒されたところでNISに送る以外に使いみちがないので成り立つ機能です。

これも先のツールに貼り付ければ内容をデシリアライズできます。

(まぁあんまり有効な利用方法がいまのところ見出されてはいませんが…面白い機能なんですけどねぇ)

まとめ

というわけで、NISが暗号化されていない平文をやり取りするHTTPで通信している事自体は特に問題はないわけです。

というか、そんなに簡単に書き換えられたらnemネットワークが暗号通貨としてなんて成り立ちません。

NISにHTTPS通信が必要とされる理由

と、nemネットワークとの通信においてはその通信経路がどうであれ問題はないのですが、ここでブラウザのセキュリティが関係してきます。

今日の最新のブラウザにおいて、HTTPSのサイトからHTTPを行うと、暗号化されていたサイトから暗号化されていないサイトへの通信が発生することになるため、通信をブロックする機能が実装されています。

今、Webサイトは常時SSL化の流れが進んでおり、Googleが検索ランクに加味するとのことでSEO対策としてどんどんHTTPS化されていくでしょう。

また、WebRTCやPWA等次世代技術を使うためには、それらが利用するJavaScript APIが強力(デバイスを制御したり、ストレージにアクセスしたり)なため、そのアプリケーションのソースコードが改ざんされていないことを保証するためにHTTPSを要求します。

それらサイトやアプリケーションからNISへアクセスするときに、HTTPでのNISへの通信がブロックされてしまいます。

上記技術と併用したnemのサービスを作るためにはHTTPSに対応したNISが不可欠だということになります。

この問題の解決方法としては、

  • NISがHTTPSで応答できるようにする
  • HTTPS通信を受けるプロキシサーバを用意してNISへ橋渡しする

などの手段が考えられます。

前者はLet’s Encryptを利用することで定期的な更新が必要ですが、無料でSSL化することができます。(あと最近出てきた認証局があったような…名前を忘れてしまった…)

httpsプロトコルに応答するNISサーバの構築

後者は自分が運営するサービスの一部としてプロキシサーバを管理することになるでしょう。

どちらが良いかは状況によりますし、バックエンドでNISと通信するアプリケーションAPIを作るなど、他にも手段はあるかもしれません。

個人的にはcatapultもいいんですが、HTTPに依存する以上、これからのHTTPの進化にもきちんと追い付いていかないとヤバいんじゃないかという心配をしています。

もっともこれはWebフロントエンド(ブラウザ)から使う場合の話であって、アプリやサーバサイドからのリクエストであればHTTPでも問題ありません。

そのような環境においては無縁な話ではありますが、せっかくHTTP通信をつかうのだからフロントエンドがダメダメなのは非常に勿体無い話です。

(サーバサイドだってHTTP2プロトコルのネットワークパフォーマンス向上の恩恵を得られないのもシンドイくなるかもしれませんね)

というわけで、ここらへんについても何かしらよいソリューションをスーパーなコアエンジニア様には何卒お願いしますという感じです。

HTTPS対応ノード

自分でメンテするのもよいですが、Shohei Kamon(加門 昭平)(@cameong)さん | TwitterのメンテしているSNがHTTPS対応してくれているので、こちらを利用させていただくと良いと思います。

なんと、18台も!(執筆時点)

おまけ:秘密鍵が漏れる可能性について

以下、実のところ未検証ではありますが、起こりうるんじゃないかなぁということです。

ちゃんと検証できたらその結果を書こうと思います。

HOSTSの書き換えによるハッキング

NISに送るトランザクションデータは先に見たようにシリアライズされたデータと、その署名という形式でしたが、実は署名をせずにNISに送金依頼を送る方法があります。

ローカルホストに立てたNISに対しては、シリアライズしていないオブジェクト形式のデータと秘密鍵を送ることでトランザクションを発行できます。

このAPIはローカルホストからのリクエストにしか応答せず、外部からの呼び出しについてはエラーを返すようになっています。

この時hostsファイルの改ざんによりlocalhost宛のリクエストが外部のサーバに指定されており、秘密鍵を送信させられる攻撃が考えられます。

もっともhostsファイルが改ざんされているサーバやマシンがもう論外という話ではありますが。

単なるミスだけど一発アウト

ローカルホスト向けにこのAPIを使ったつもりが実は外部サーバのAPIを叩いてしまって、インターネット上に秘密鍵を含んだリクエストが流れてしまった場合も漏れてしまったと考えて良いでしょう。

開発中などくれぐれも注意するようにしましょう。