エンジニアリング

SSH ホストキー TOFU と、モバイルクライアントが気にすべき理由

Trust On First Use(TOFU)ホストキー検証の実用ガイド。多くのモバイル SSH アプリが間違っているポイントも整理。

CC Chen Chen· ファウンダー·2026 年 4 月 30 日·約 10 分

TOFU が実際に意味するもの

Trust On First Use(初回接続時の信頼)は、最初にサーバーと通信したときにその識別子を記録しておくセキュリティモデルです。それ以降、別の識別子で応答してくる接続は怪しいと判断します。

SSH の場合、識別子はサーバーの公開ホスト鍵——通常は Ed25519 か RSA のフィンガープリント——です。prod-server.example.com に初めて SSH 接続すると、サーバーが公開鍵を提示します。あなたはそれを信頼するか(自分で立てたサーバーだから)、信頼しないか(自分で立てたわけではなく、誰かがなりすましている可能性)を判断します。一度信頼すると、フィンガープリントは known_hosts ファイルに保存されます。それ以降の接続では同じ鍵が提示される必要があり、そうでなければ OpenSSH は接続を拒否します。

TOFU は完璧ではありません——初回接続は未検証です——が現実的です。最初のハンドシェイクのあとは、敵対的なネットワーク上でもアクティブな中間者攻撃から守られます。

モバイルで特に重要な理由

モバイル端末は 1 日のうちに、信頼できるネットワークと信頼できないネットワークの間を何度も行き来します:自宅 Wi-Fi、オフィス Wi-Fi、モバイル回線、明らかに怪しいキャプティブポータルのあるホテルのネットワーク、突然「Free WiFi Login」という名前になっているカフェのネットワーク。どれもが、攻撃者が経路上に潜んで目的の SSH サーバーになりすませる場所です。

SSH クライアントがサーバーのホスト鍵をきちんと保存していれば、こうした状況でも問題ありません。攻撃者の鍵は一致せず、接続はそこで失敗します。逆にクライアントがホスト鍵を保存しない(あるいは毎回サーバーから送られてきたものをそのまま受け入れる)場合、何の保護もありません。ポート 22 で応答した相手が誰であれ、認証情報を渡してしまうことになります。

多くのモバイルクライアントがやっている(残念な)こと

TermAI の挙動を設計する際に、人気のあるモバイル SSH アプリをいくつかテストしました。よく見られるデフォルトの挙動:

  • 初回接続時に確認、以降は再検証しない。初回接続でフィンガープリントのダイアログが出る。受け入れをタップ。鍵が記録される。以降の接続は黙って一致を確認する。これは OpenSSH と同じ挙動で——正しく実装されていれば問題ありません。
  • 初回接続時に確認するが、UI が紛らわしい。上と同じだが、ダイアログには「サーバーを検証できません」のような文言と「受け入れる」「キャンセル」のボタンがあるだけ。多くのユーザーは反射的に受け入れをタップします。さらに悪いのは、あとで鍵が変わったときも同じダイアログが出て、ユーザーがまた受け入れてしまうこと。鍵の変更は初回接続とはまったく違う見た目であるべきです。
  • そもそも何もしない。フィンガープリントを一度も表示せず、鍵の変更も警告しない——ただ接続するだけのアプリ。これは設計の時点で壊れています。

3 番目のカテゴリは予想以上に多く、有料アプリにもありました。

OpenSSH はどう実装しているか

OpenSSH の known_hosts ファイルがリファレンス実装です。挙動はこうです:

# 初回接続
$ ssh prod-server
The authenticity of host 'prod-server' can't be established.
ED25519 key fingerprint is SHA256:abc...xyz.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'prod-server' to the list of known hosts.

# あとで、鍵が変わると
$ ssh prod-server
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Host key verification failed.

初回の確認は穏やか。鍵の不一致は敵対的で、接続を拒否します。続けるには、古いエントリを手動で削除する必要があります。この非対称性——初回は穏やかに、変更には強硬に——を TermAI も継承しています。

TermAI はどう実装しているか

ホストフィンガープリントは、端末上の AES 暗号化された Hive ボックスに保存します。暗号鍵は iOS Keychain(Android では Keystore 相当)に格納されており、データベースファイルを端末から取り出されても、システムのキーストアが解錠されなければ読み取れません。

  1. 初回接続。サーバーがホスト鍵を提示すると、TermAI は SHA256 フィンガープリントを計算して SHA256:abc...xyz のように表示します。タップでコピー可能。受け入れがデフォルトボタン、キャンセルもはっきり表示されています。受け入れるとフィンガープリントが保存されます。
  2. 以降の接続。フィンガープリントが保存値と一致すれば、接続は黙って成立します。プロンプトも警告も中断もなし。
  3. 以降の接続で不一致が発生したとき。強制停止。赤いダイアログ。何が起きているか、保存されたフィンガープリントと新しいフィンガープリントの両方を示し、意図的にワンタップ受け入れボタンは置きません。続けるには 設定 → Known Hosts に行って明示的に保存済みフィンガープリントを削除し、もう一度接続してもう一度信頼する必要があります。意図して 2 段階にしています。

2 段階であることが重要です。鍵不一致の警告をワンタップで上書きできてしまうと、時間に追われたユーザーはそのまま押し切ってしまいます。中断そのものが保護なのです。

実際の MITM を見抜く

クライアントが鍵の変更を警告したら、問うべきは:サーバーの鍵が正当な理由で本当に変わったのか、それとも接続を攻撃する何かが起きているのか。

正当な理由:

  • サーバーの OS を再インストールした
  • 意図してホスト鍵を更新した
  • DNS でホスト名を別のサーバーに向け直した
  • サーバーがロードバランサ配下にあり、別のバックエンドに当たった(全バックエンドに同じ鍵を設定すべき)

怪しい理由:

  • 完全には信頼していない新しい Wi-Fi につないでいる
  • 予期しないキャプティブポータルが出た
  • サーバー管理者から鍵変更の連絡がない
  • 正規サーバーで確認しても、このフィンガープリントが完全に未知

迷ったときの正解:接続しない。サーバーの Web コンソール/クラウドプロバイダのシリアルコンソール/同一ネットワーク上の信頼できるマシンを開き、サーバーに対して ssh-keyscan を実行してフィンガープリントを比較します。クライアントが見ている新しい鍵と一致するなら問題なし。一致しないなら MITM と話しています。

正当な鍵の変更

正当にホスト鍵を更新する場合(ベストプラクティス:2 年ほどに一度)、全クライアントを混乱させずに行うクリーンな方法があります:

  1. 新しいホスト鍵を生成しつつ、古い鍵も受け入れたままにする
  2. 移行期間中は sshd に両方を提供させる
  3. 新しいフィンガープリントを、ユーザーが信頼できる経路(チームのパスワードマネージャ、署名付きアナウンス)で配布する
  4. 全員の更新が終わったら、古い鍵を削除する

個人サーバーなら単純に「火曜にホスト鍵を更新します。新しいフィンガープリントは XX です」と関係者に伝えるだけで十分です。一声かけるだけで、警告ダイアログを予期された確認に変えられます。

クイックファクト

  • TOFU = 初回使用時の信頼:OpenSSH と同じホストキーモデル
  • 接続ごとにキーを固定:変更時は新旧両方のフィンガープリントを表示して警告
  • 帯域外で検証:サーバーで ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub を実行し、照合してから受け入れる
  • 中間者攻撃対策:キーの変更は中間者攻撃そのものの兆候なので、自動では受け入れない
  • 検証を無効にしない:StrictHostKeyChecking no は中間者攻撃への唯一の防御を外す
TermAI を試す

iOS / Android で無料。無料プランは AI 5回/日、SSH/SFTP 無制限、Tailscale 内蔵。

CC
Chen Chen — Founder of TermAI

Writes about mobile DevOps, terminal UX, and the surprising depth of "boring" infrastructure.

Was this useful? ← Back to blog