Open3

非SSLなローカルデバイスとWebRTCでセキュアに通信したい^2

okuokuokuoku

そろそろ具体的なプロトコルを考えたい。

エンティティ

browser は通常のWebブラウザである。SecureサイトのhttpsサイトのためのPKI証明書を持つ。Deviceに対してはhttps接続はできない。

Secure はGitHub pagesのような静的HTTPSプロバイダで提供されるWebページとする。LocalStorage等は使用してよいが、外部の動的Webサービスには依存してはいけない。

Device はInsecureなHTTPサーバを提供するデバイスである。libdatachannelによるWebRTC datachannelも同じデバイスで提供し、ユーザーの browser とこの device でセキュアな接続を確立することが目的となる。

prev

https://zenn.dev/okuoku/scraps/bc1445a903fb7e

okuokuokuoku

プロトコル

プロトコルは "ペアリング" と "シグナリング" の2段階に分かれる。

ペアリング

最初に、 SecureDevice をペアリングし、Secureのサイトに関連付けられたパスワードマネージャーに秘密鍵と公開鍵の鍵ペアを記憶させる。

  1. ユーザーはサイト Secure を表示する。SecureはJWTのための鍵ペアを生成し、ユーザーに示す(passwordなformに入れておく)。
  2. Secure に表示された公開鍵を Device にセットする (Out of boundで)
  3. Device は自身の公開鍵をURLのフラグメント部に入れた Secure へのリンクを生成して、ユーザーにクリックさせる。 フラグメント部は Secure 側の公開鍵で常に暗号化する
  4. Secure1) Secure側の公開鍵と秘密鍵 2) Device側の公開鍵 をセットにしたフォームを生成して、ユーザにパスワードマネージャに覚えさせた鍵情報を更新させる。

1 は、要するにパスワードマネージャーに秘密鍵を入れさせたい。 ...普通 POST でページ遷移させないとダメなんじゃないかという気がするが、少くともChromiumはXHRでもstateをpushすれば可能らしい。

https://bugs.chromium.org/p/chromium/issues/detail?id=43219

https://stackoverflow.com/questions/21191336/getting-chrome-to-prompt-to-save-password-when-using-ajax-to-login/33113374#33113374

https://web.dev/i18n/ja/sign-in-form-best-practices/

最悪Service workerでPOSTをhookすれば行けるか。。?

2 と 3 も割と一筋縄で行かない。同じPCで動くプログラムだったらコピペで良いけど、スマホだったらQRコードにするとか工夫が居る。

特に、公開鍵のセットはOut of boundでやる必要がある。素のhttpには、なりすましや盗聴のリスクがあるので通常考えられるであろう:

  1. SecureDevice のIPv4アドレスとポートを設定する
  2. Secure がURLを生成してクリックさせる

というフローはあまり安全でない。まぁ割りきってしまっても良いのかもしれないけど。

安全性という面では Device 起点の方が安全になるが、コピペするときのサイズが大きくなってしまうので避けた方が良い気がしている。Bluetoothのような6桁番号比較とかでも良いかもしれない。

シグナリング

シグナリングはWebRTCの普通のシグナリングと大差ない。通常のWebRTCではWebSocketが使われることが多いが、WebSocketはTLS通信しなければならない都合上 Device 側に有効なPKI証明書が必要であまり実用的でない。ここでは、 http URLのフラグメント部分を使用して通信する。

  1. ユーザーは Secure にデバイスのIPv4アドレス、またはIPv6アドレスを入れ、パスワードマネージャーが覚えているデバイス名を選択してから "接続" ボタンを押す。公開鍵 / 秘密鍵のペアはパスワードマネージャーが入力する。
  2. Secure はICEのオファーを作成してJWTを作成し、鍵ペアを用いて署名する
  3. 署名されたJWTを含んだ Device のhttp URLを生成して、ユーザーにクリックさせる。近代的なブラウザでも、 http へのリンクは1クリックで遷移できる。
  4. Device もICEを生成して、公開鍵で署名 + Secureの公開鍵で暗号化する。
  5. 署名 + 暗号化されたICEデータをJWTにして Secure サイトのURLのフラグメント部にセットし、遷移させる。
  6. 両者のICEデータが揃うので、WebRTCチャンネルを確立する

やっている事は昔なつかしOAuth2のimplicit flowに近い。Implicit flow自体は今や使われることのない https://qiita.com/TakahikoKawasaki/items/8567c80528da43c7e844#インプリシットフロー非推奨 技術だけど、https ←→ http サイトではCORSを組めないのでフラグメント渡しをやらざるを得ない。