このページは翻訳です。英語版と内容が異なる場合は、英語版が優先されます。 英語版
データを守るしくみ
私たちを信頼してください、とは言いません。このページでは、何が暗号化されるのか、私たちのサーバーに何が見えて何が見えないのか、そしてその一つひとつを自分で確かめる方法を、正確にお伝えします。
1分でわかる要点
- サーバー、ログイン情報、鍵、メモは、AES-256-GCMで暗号化され、デバイス上の1つのファイルに保管されます。アプリの見た目やRDPサーバーと共有するフォルダーなど、このデバイスだけのための一部の設定は、暗号化されずにアプリの設定に保存され、同期されることはありません。
- 鍵は、プロファイルパスワードからArgon2idで作られます。Argon2idは、推測にかかるコストが高くなるよう、あえて処理を遅くしてある方式です。パスワードがどこかに保存されたり送信されたりすることはありません。
- 同期をオンにすると、各アイテムはアップロードの前にデバイス上で暗号化されます。サーバーは、自身では復号できないデータを保存します。
- ただし、サーバーにはデータの「外側」が見えます。メールアドレス、いつどのIPアドレスから同期したか、種類ごとのアイテムの数と大きさ、プロファイル名、そして共有プロファイルの場合は共有名とメンバーのメールアドレスです。
- アプリには、広告も、アナリティクスも、クラッシュレポートもありません。
サーバーが読めないもの
- アイテムの中身:名前、アドレス、ポート、ユーザー名、パスワード、秘密鍵とそのパスフレーズ、トンネル、メモ、コマンド、フォルダー名
- どのログイン情報や鍵が、どのサーバーのものか
- 誰がアイテムを変更したか
- プロファイルパスワード、共有用パスフレーズ、プロファイルを開く鍵
サーバーに見えるもの
- メールアドレスと、サインイン時のアカウントパスワード(保存するのはそのbcryptハッシュだけです)
- サインインや同期を行ったときのIPアドレスと日時
- プロファイル内の種類ごとのアイテムの数、それぞれのサイズ、各アイテムがいつ、どのデバイス(ランダムなID)で変更されたか
- プロファイル名
- 共有プロファイルの場合:共有名、メンバーのメールアドレスと役割
同期のときに起きること
1. デバイス上で
プロファイルは、1つにつき1つのファイルです。中身は、サーバー、ログイン情報、SSH鍵、メモ、設定を収めた小さなデータベースです。データベース全体を圧縮してから、AES-256-GCMで暗号化します。ファイルを保存するたびに、新しいランダムなノンスを使います。また、ファイルはまず一時的なコピーに書き込まれるため、クラッシュしても書きかけのファイルが残ることはありません。
鍵は、プロファイルパスワードからArgon2idで作られます。設定はメモリ64 MiB、パス数3、レーン数4で、ソルトはプロファイルごとにランダムです。これらの設定はアプリに固定されているため、ファイルを書き換えても設定を弱めることはできません。
パスワードがディスクに書き込まれたり、どこかに送信されたりすることはありません。鍵を作ったあと、アプリのコアにあるパスワードのコピーは消去されます。鍵も、プロファイルをロックしたときに消去されます。ロックすると、すべての接続も閉じられ、ログインの途中のものは中止されます。パスワードを忘れると、そのプロファイルは開けません。プロファイルを開けるものはどこにも保管されていないため、リセットする方法もありません。
クイックロック解除(Quick unlock)は任意の機能で、初期状態ではオフです。WindowsではWindows HelloとTPMを、Androidでは指紋センサーとAndroid Keystoreを使います。システムが保管するのはプロファイルの鍵で、パスワードを保管することはありません。また、この鍵が同期されることもありません。Androidでは、システムによるアプリデータのクラウドバックアップをオフにしています。
Windowsで、パスワードを保存してあるサーバーをリモート デスクトップ接続で開くとき、アプリは短時間だけ存在する.rdpファイルでパスワードを渡します。ファイル内のパスワードは、Windows DPAPIによって、お使いのWindowsユーザー用に暗号化されます。このファイルは、リモート デスクトップ接続が読み込むとすぐに削除されます(遅くとも30秒後。クラッシュによって残ったファイルは、次回の起動時に削除されます)。
- 鍵導出(Argon2idの設定) lib.rs:237–254
- 新しいノンスを使うAES-256-GCM lib.rs:256–263
- プロファイルファイルの保存 lib.rs:431–486
- リモート デスクトップ接続に渡すパスワード(DPAPI) rdp.rs:178–193
2. クラウド同期(Cloud sync)
同期は、サインインするまでオフです。各プロファイルには、それぞれ専用のランダムな256ビットのデータ鍵があり、暗号化されたプロファイルの中に保存されています。アップロードの前に、すべてのアイテム(サーバー、ログイン情報、鍵、メモなど)が、この鍵で1つずつ個別に暗号化されます。暗号化にはAES-256-GCMと、新しいランダムなノンスを使います。
サーバーは、暗号化された各アイテムと一緒に、ランダムなID、アイテムの種類、タイムスタンプ、削除されたかどうかを示すフラグを受け取ります。タイムスタンプには、変更を行ったデバイスのランダムなIDが含まれます。暗号化されたアイテムのサイズから、元のアイテムのおおよその大きさがわかります。サーバーは、各プロファイルの名前も保存します。既知のホスト鍵、コマンド履歴、監視の設定、そしてこのデバイスだけのために保存される設定が同期されることはありません。
1つのアイテムについて、サーバーが保存する内容は次のとおりです。 (例)
{
"uuid": "6f1c2e0a9b7d4c3e8a5f0d1b2c3e4f50",
"entity_type": "servers",
"updated_at": "001761043562118:00000:9f3a61c2d4e5b7a8",
"deleted": false,
"blob": "4e8a1f0c2b9d3e7a5c6f8b1d0a2e4c6b8d0f1a3c5e7b9d1f…"
}- 同期する各アイテムの暗号化 lib.rs:1050–1056
- 各アイテムについて送信される内容 lib.rs:1086–1124
3. 新しいデバイスでプロファイルを開く
同期したプロファイルを別のデバイスで開くには、そのデバイスにプロファイルのデータ鍵が必要です。そのため、サーバーはもう1つレコードを保管します。プロファイルパスワードから同じArgon2idの設定で作った鍵で暗号化した、データ鍵です。サーバーはこれを開けません。
万一サーバーのデータが盗まれた場合に攻撃者が試みることのできる主な手段も、このレコードを使ったものです。つまり、プロファイルパスワードをオフラインで推測することです。推測するたびにArgon2idを丸ごと1回実行する必要があるため、ほかでは使っていない長いパスワードにしておけば、データは安全に守られます。
- 保管されるプロファイルの鍵のコピー lib.rs:1551–1569
- 新しいデバイスでそれを開く処理 lib.rs:1574–1594
4. アカウントパスワード
同期アカウントには、プロファイルパスワードとは別の、専用のパスワードがあります。このパスワードはサインイン時にHTTPS経由でサーバーに送られ、サーバーはそのbcryptハッシュだけを保存します。パスワードを5回間違えると、パスワードによるサインインが15分間停止します。
このサービスはCloudflareの背後に置かれており、HTTPS接続はCloudflareが代わりに処理しています。そのため、サーバーと同じように、Cloudflareもサインイン時にアカウントパスワードを見ることのできる立場にあります。ただし、アイテムについては暗号化された形でしか見えません。
プロファイルパスワードを、アカウントパスワードとして使うことは絶対に避けてください。そうすれば、サーバーやCloudflareが目にするどんな情報を使っても、プロファイルは開けません。
- サインインのリクエスト cloud.rs:105–108
6. サーバーに悪意があったら
よいセキュリティとは、サーバーが本来するはずのことだけを前提にするものではありません。サーバーを完全に掌握した人に何ができて、何ができないのかを以下に示します。
できないこと
- アイテムを読むこと、または気づかれずにその中身を変えること
- プロファイルパスワードなしで、保管されたプロファイルの鍵のコピーを開くこと
- アプリが送信することのない、プロファイルパスワードや共有用パスフレーズを知ること
- お使いのデバイス上のアプリを変えること:自動更新はなく、アプリは新しいバージョンがあることを表示するだけです
できること
- 弱いプロファイルパスワードを、オフラインで推測しようとすること(推測1回ごとに、時間のかかるArgon2idの実行が1回必要)
- 次にサインインしたときに、アカウントパスワードを入手すること
- アイテムを削除したり、古いバージョンに戻したり、変更を届けずに止めておいたりすること。なお、同期で何かが削除される前に、アプリはデバイス上にプロファイルのコピーを残します
- 共有プロファイルに誰かを招待するときに、間違った公開鍵を渡すこと
- 上の表に挙げた情報を見ること
7. アプリのすべての接続先
接続先は、自分のサーバーと、そのために設定したプロキシや踏み台ホストです。同期サーバーには、サインインしているときだけ接続します。そしてGitHubには、新しいバージョンを確認するために接続します。プロファイル一覧が開いたときと、「Check for updates」を押したときに、アプリはGitHubで公開されているリリース一覧を問い合わせます。このリクエストに含まれるのは、アプリの名前とバージョンだけです。
アプリには、アナリティクス、クラッシュレポート、広告のコードはありません。同期については、アプリは内蔵の認証局リストだけを信頼し、リダイレクトにも従いません。そのため、知らないうちに別のサーバーへ向けられることはありません。
- 更新の確認 about.rs:75–139
8. リモートデスクトップにデバイスから渡るもの
クリップボード。RDPやVNCのタブに貼り付けたテキストは、そのサーバーに送られます。貼り付けるまでは、サーバーには何も渡されません。Share the clipboardがオンのとき(初期設定でオンで、サーバーごとに切り替えられます)、リモートデスクトップでコピーしたテキストが、このデバイスのクリップボードに入ります。渡るのはテキストだけでファイルは渡らず、VNCで送れるのはプレーンなASCIIテキストだけです。
ファイルは、コンピューター上の内蔵RDPクライアントだけで、しかもそのサーバーにフォルダーかドライブを指定した場合に限って渡ります。読み取り専用にすることもできます。サーバーからは\\tsclientの下にあるドライブとして見え、コンピューターのそれ以外の部分は見えません。サーバーが要求するパスはすべてチェックされます。..は使えず、途中にあるリンクもたどらないため、指定した範囲の外には一切アクセスできません。
これらの設定はこのデバイスに保存され、同期されることはありません。そのため、プロファイルを共有している相手が、これらをオンにすることはできません。
- 共有フォルダーのパスをその内側にとどめる処理 rdp_share.rs:121–147
- VNCのクリップボードで送れる内容 vnc_client.rs:180–188
自分で確かめる
ここに書いたことを、私たちの言葉だけで信じる必要はありません。簡単なものから上級者向けのものまで、確かめる方法を紹介します。
コードを読む
アプリはオープンソースです。このページのリンクは、ここで説明しているバージョン(
c9dec31)の、実際に処理を行っているコードの行を直接指しています。接続を観察する
ファイアウォールやネットワークモニターを使ってください。見えるのは、自分のサーバー、同期サーバー(サインインしている間だけ)、そしてプロファイル一覧が開いたときのapi.github.comです。ほかには何もありません。
サーバーが保存している内容を見る
上級者向け:アプリのデータフォルダー(cloud_token.json)からサインイントークンを取り出し、X-Auth-Tokenヘッダーに入れて、APIに自分のデータを要求します。使うのは、GET /api/sync/profiles、またはレコードのリストを空にしたPOST /api/syncです。返ってくるのは、ID、種類、日時、フラグ、暗号化されたテキストだけです。終わったら、アプリでサインアウトしてそのセッションを終了してください。
通信内容を読む
上級者向け:リクエストを本物のサーバーへ中継する小さなプロキシを自分のコンピューターで動かし、アプリの同期サーバーをhttp://127.0.0.1とプロキシのポートに設定します。アプリが平文のHTTPを許可するのは、自分のコンピューターに対してだけです。アプリが何を送っているか、そしてアイテムが暗号化されていることがわかります。(システムにプロキシの証明書を追加しても使えません。アプリは独自の認証局リストを使うためです。)
自分のデータを復号する
上級者向け:パスワードがあれば、下の表の設定を使って、Argon2idとAES-GCMに対応した任意のツールで自分のデータを開けます。パスワードがなければ、誰にも開けません。
ダウンロードしたファイルを確かめる
チェックサムをリリースのSHA256SUMS(バージョン0.4以降)と照合するか、Androidアプリの証明書をapksignerで確認するか、ソースからアプリをビルドしてください。
自分のツールで使うための設定
| 鍵導出 | Argon2id、バージョン0x13、メモリ64 MiB、パス数3、レーン数4、32バイトの鍵、16バイトのランダムなソルト |
| プロファイルファイル | “OMNV” · バージョン1 · ソルト(16バイト) · ノンス(12バイト) · zstdで圧縮したSQLiteデータベースをAES-256-GCMで暗号化したもの(16バイトのタグ付き) |
| 同期されるアイテム | hex(ノンス(12バイト) · AES-256-GCMの暗号文とタグ)、プロファイルの256ビットのデータ鍵を使用 |
| 保管されるプロファイルの鍵のコピー | hex(0x02 · ソルト(16バイト) · ノンス(12バイト) · 32バイトのデータ鍵をAES-256-GCMで暗号化したもの)、プロファイルパスワードから作った鍵を使用 |
| 共有 | X25519、HKDF-SHA256、AES-256-GCM |
このページが約束しないこと
- Submarineは、第三者によるセキュリティ監査を受けていません。これまでのレビューは、作者自身が行ったものです。
- 弱いパスワードや使い回したパスワードが、いちばんの弱点です。プロファイルパスワードには長くてほかで使っていないものを、アカウントパスワードにはそれとは別のものを使ってください。
- Telnetと通常のVNCは、仕様上暗号化されません。アプリはそれらに目印を付けて表示し、SSHサーバーを経由して使うこともできます。通常のVNCでは、貼り付けたクリップボードのテキストや、サーバーでコピーされたテキストも、暗号化されずにネットワークを流れます。
- 内蔵のRDPクライアントは試験的な機能で(リリース版ではなく、IronRDPの特定のコミットからビルドしています)、サーバーごとに選ぶまではオフです。
- このページは、
c9dec31時点のアプリについて説明しています。コードが変われば、このページもそれに合わせて変わります。