SSH の仕組み - 鍵交換・ホスト鍵検証・公開鍵認証を一次ソースで理解する

SSH の仕組み - 鍵交換・ホスト鍵検証・公開鍵認証を一次ソースで理解する

作成日:
読了:18
更新日:
この記事を読む人におすすめPR / Amazonアソシエイト

当サイトは Amazon.co.jp を宣伝しリンクすることで紹介料を得る手段を提供する、Amazonアソシエイト・プログラムの参加者です。価格・在庫はリンク先の最新情報をご確認ください。

サーバーにログインするとき、ファイルを転送するとき、Git で push するとき。その裏で当たり前のように使われているのが SSH(Secure Shell)です。パスワードを入力するか、あるいは鍵ファイルさえあれば、離れた場所のマシンに安全につながる。その「安全に」の中身を、なんとなくの理解のまま使っている人は少なくありません。

この記事では、SSH がどのように接続を確立し、通信相手を確かめ、ユーザーを認証しているのかを、一次ソースである RFC 4251 / 4252 / 4253 / 4254(SSH-2 の Architecture / Authentication / Transport / Connection の4本)を軸に整理します。鍵交換、ホスト鍵の検証、公開鍵認証、そしてポートフォワーディングまで、OpenSSH での確認方法も添えて一通り押さえます。

NOTE

本記事は手順・用語・アルゴリズム名を RFC 4251〜4254 などの一次ソースと OpenSSH の公式 man ページを参照して整理しています。実装や設定による差異、確定できない箇所については「未確認」と明示します。

SSH とは何か(まず全体像)

SSH は、信頼できないネットワークの上で安全なリモートログインとコマンド実行、その他のネットワークサービスを提供するためのプロトコルです。RFC 4251(The Secure Shell (SSH) Protocol Architecture)は、SSH-2 を次の3つの層で構成すると定義しています。

  • トランスポート層プロトコル(RFC 4253): サーバー認証、機密性(暗号化)、完全性を提供する。通常は TCP/IP の上で動く
  • ユーザー認証プロトコル(RFC 4252): トランスポート層の上で、クライアント側のユーザーをサーバーに対して認証する
  • 接続プロトコル(RFC 4254): 暗号化されたトンネルを複数の論理チャネルに多重化し、対話シェルやポートフォワーディングを実現する

つまり SSH は、「まず安全な土管を作る(トランスポート層)」「その中で誰がログインするかを確かめる(認証層)」「その上で複数の用途を同時に流す(接続層)」という三段構えになっています。この分担を頭に入れておくと、以降の話が整理しやすくなります。

なお、既定の待ち受けポートは TCP 22番で、IANA が SSH に割り当てています(RFC 4253)。かつての SSH-1(プロトコルバージョン 1.x)は設計上の弱点があり、現在は使われません。本記事は現行の SSH-2 を対象とします。

接続確立の流れ(TCP から認証まで)

まず、クライアントがサーバーにつながってからログインが済むまでの全体の流れをシーケンス図で見ます。

Loading diagram...

一番下で TCP がつながる部分は、TCP と UDP の 3ウェイハンドシェイクそのものです。SSH はその上に構築されるアプリケーション層のプロトコルだと捉えると位置づけが分かりやすくなります。

バージョン交換とアルゴリズムのネゴシエーション

TCP がつながると、双方はまず識別文字列(identification string)を送り合います。RFC 4253 が定める形式は SSH-protoversion-softwareversion SP comments CR LF で、SSH-2 では protoversion が 2.0 になります。実際には SSH-2.0-OpenSSH_10.4 のような文字列が流れます。この文字列は最大255文字(CR と LF を含む)と定められています。

続いて双方が SSH_MSG_KEXINIT を送り、鍵交換・ホスト鍵・暗号・MAC・圧縮それぞれについて、対応アルゴリズムの名前リスト(name-list)を提示します。各リストの先頭が最優先で、両者の候補を突き合わせて実際に使う組み合わせを決めます。ここで「サーバーとクライアントが対応アルゴリズムを一つも共有していない」と接続は成立しません。古い機器につなげないときに、まずこのネゴシエーションを疑うのはそのためです。

RFC 4253 が相互運用のために必須としている鍵交換方式は diffie-hellman-group1-sha1diffie-hellman-group14-sha1 で、いずれも SHA-1 を使う古いものです。現在の OpenSSH は、より新しい curve25519-sha256(Curve25519 を用いた鍵交換、RFC 8731 で標準化)などを既定で優先します。SHA-1 ベースの旧方式は安全性の観点から無効化が進んでいます。

ホスト鍵の検証(known_hosts と TOFU)

鍵交換の過程で、サーバーは自分のホスト鍵(host key)で署名を行い、クライアントは「つないだ相手が確かに本物のサーバーか」を確かめます。これがなければ、経路上の攻撃者がサーバーになりすます中間者攻撃(MITM)を防げません。この「公開鍵で署名を検証し、秘密鍵の所有を確かめる」構図は、公開鍵暗号と電子署名の考え方そのものです。ホスト鍵のアルゴリズムには、RFC 4253 時点では ssh-dss(必須)や ssh-rsa(推奨)が定義され、現在は ssh-ed25519(Ed25519、RFC 8709 で標準化)が広く使われています。

問題は「初めてつなぐサーバーの鍵が本物かをどう知るか」です。RFC 4251 は、ホスト鍵の信頼モデルとして2つを挙げています。

モデル仕組み特徴
ローカルデータベースホスト名と公開ホスト鍵の対応をクライアント側に保存中央インフラ不要だが、鍵の管理・更新は自分で行う
認証局(CA)ホスト名と鍵の対応を信頼された CA が証明管理は楽だが、中央インフラへの依存が生じる

OpenSSH で普段使われるのはローカルデータベース方式で、その実体が ~/.ssh/known_hosts です。初めて接続するホストでは、次のようにフィンガープリントの確認を求められます。

The authenticity of host 'example.com (203.0.113.10)' can't be established.
ED25519 key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

ここで yes と答えると、その鍵が known_hosts に保存され、次回以降は自動で照合されます。この「初回だけは相手を信じて、以降はその鍵を覚えておく」方式は TOFU(Trust On First Use)と呼ばれます。

RFC 4251 は、この初回接続の扱いを明確な弱点として認めています。初回に鍵検証を省けることは移行期の利便性を高めますが、その一回が MITM に晒されると攻撃者の鍵を覚え込んでしまいます。だからこそ、初回のフィンガープリントは別経路(構築ログや管理コンソール)で照合するのが望ましいです。なお、2回目以降でフィンガープリントが変わっていた場合、OpenSSH は大きな警告を出して接続を止めます。これはサーバー再構築で鍵が変わっただけのこともありますが、なりすましの可能性も含めて必ず原因を確認すべきサインです。

WARNING

「鍵が変わった」という警告が出たときに、理由を確かめずに known_hosts の該当行を消して再接続するのは危険です。正当なサーバー鍵の変更なのか、経路上のなりすましなのかを、必ず別の手段で確認してから対応してください。

公開鍵認証の仕組み(authorized_keys と署名)

土管が安全になり相手も確かめられたら、次はユーザーの認証です。RFC 4252(The Secure Shell (SSH) Authentication Protocol)は認証方式として publickey(必須)、passwordhostbasednone を定義しています。実務で推奨されるのは 公開鍵認証(publickey)です。

公開鍵認証は、大きく2段階で進みます。

  1. 問い合わせ: クライアントが「この公開鍵で認証できますか」と、署名なし(フラグを偽)で打診する
  2. 認証: 受理されそうなら、クライアントは署名付き(フラグを真)で認証要求を送る

肝心なのが2段階目の署名の中身です。RFC 4252 によれば、クライアントはセッション識別子(最初の鍵交換で導出される値)に加え、メッセージ種別・ユーザー名・サービス名・方式名・鍵アルゴリズム・公開鍵そのものを、この順に並べたデータへ秘密鍵で署名します。サーバーはその署名を公開鍵で検証します。

ここでのポイントは、秘密鍵はネットワークに一切流れないことです。流れるのは署名だけで、しかも署名対象にはコネクション固有のセッション識別子が含まれるため、盗んだ署名を別の接続で使い回すこともできません。パスワード認証(password)はパケット全体が暗号化されるとはいえ、パスワードの推測・使い回し・漏洩のリスクが残ります。公開鍵認証がインフラ運用で標準とされる理由がここにあります。

サーバー側は、ログインを許可する公開鍵を ~/.ssh/authorized_keys に列挙して管理します。鍵ペアは ssh-keygen で作成します。OpenSSH の現行 man ページによれば、ssh-keygen を引数なしで実行すると Ed25519 の鍵が生成され、-t で指定できる鍵種別は ecdsa / ecdsa-sk / ed25519 / ed25519-sk / mldsa44-ed25519 / rsa です(DSA は一覧から外れています)。

# Ed25519 の鍵ペアを作成(コメントを付けておくと後で識別しやすい)
ssh-keygen -t ed25519 -C "you@example.com"
 
# 公開鍵をサーバーの authorized_keys に登録する
ssh-copy-id user@example.com

ssh-keygen は秘密鍵(例: ~/.ssh/id_ed25519)と、末尾に .pub が付いた公開鍵(~/.ssh/id_ed25519.pub)を生成します。サーバーに渡すのは .pub の公開鍵だけで、秘密鍵は手元から出しません。秘密鍵にはパスフレーズを付け、後述の ssh-agent と組み合わせると、毎回の入力を省きつつ鍵ファイル単体の漏洩リスクを下げられます。

接続の多重化とポートフォワーディング

認証が済むと、いよいよ接続プロトコル(RFC 4254)の出番です。RFC 4254 は「すべての端末セッション、フォワードされた接続などはチャネルである」と述べています。1本の暗号化されたコネクションの中に複数のチャネルを多重化し、それぞれをウィンドウサイズで流量制御しながら並行して扱います。チャネルは SSH_MSG_CHANNEL_OPEN で開き、SSH_MSG_CHANNEL_DATA でデータを流し、SSH_MSG_CHANNEL_CLOSE で閉じます。だから1つの SSH 接続で、シェル操作をしながら同時にポート転送を走らせる、といったことができます。

そのポートフォワーディングを OpenSSH のオプションで整理すると次のようになります。

オプション名前向き用途の例
-Lローカルフォワード手元の待受ポート -> リモート経由で目的地へ手元から社内 DB にトンネル経由で接続
-Rリモートフォワードサーバーの待受ポート -> 手元経由で目的地へ手元の開発サーバーを外部へ一時公開
-Dダイナミックフォワード手元に SOCKS プロキシを立てる通信をまとめて SSH 経由に流す

具体的なコマンド例です。実務での確認ポイントも添えます。

# ローカルフォワード: 手元の 15432 を、サーバー経由で db.internal:5432 へ
ssh -L 15432:db.internal:5432 user@bastion.example.com
 
# リモートフォワード: サーバーの 8080 を、手元の localhost:3000 へ
ssh -R 8080:localhost:3000 user@example.com
 
# ダイナミックフォワード: 手元の 1080 に SOCKS プロキシを立てる
ssh -D 1080 user@example.com

-L の書式は [bind_address:]port:host:hostport で、「手元の port への接続を、サーバー側から見た host:hostport へ転送する」と読みます。踏み台(bastion)越しに内部ネットワークのサービスへ届くのは、目的地の名前解決と接続がサーバー側で行われるからです。-R はその逆向きで、手元のサービスをサーバー側の待受ポートに露出させます。外部公開になり得るため、-R を使うときは待ち受けアドレスと公開範囲に注意が必要です。

実務での設定と確認

接続先が増えてくると、毎回長いオプションを打つのは現実的ではありません。~/.ssh/config にホストごとの設定をまとめておくと、ssh myserver の一行でつながります。

Host myserver
    HostName example.com
    User deploy
    Port 22
    IdentityFile ~/.ssh/id_ed25519

Port の既定値は22なので、標準ポートなら省略できます。別ポートで待ち受けているサーバーには、コマンドラインなら ssh -p 2222 user@host のように -p で指定します(-L などの数値ではなく接続先ポートの指定です)。

鍵の扱いを楽にするのが ssh-agent です。パスフレーズ付きの秘密鍵を一度 ssh-add で登録しておけば、以降はエージェントが署名を代行し、毎回の入力が不要になります。エージェントはメモリ上で鍵を扱い、秘密鍵ファイル自体を各所へばら撒かずに済む点が利点です。

うまくつながらないときは、詳細ログが手掛かりになります。

# 認証や鍵交換のどこで止まっているかを詳細表示する
ssh -v user@example.com
 
# さらに詳しく(-vvv まで指定できる)
ssh -vvv user@example.com

-v の出力には、ネゴシエーションで選ばれた鍵交換・ホスト鍵・暗号のアルゴリズム、どの鍵ファイルで認証を試みたか、known_hosts の照合結果などが順に並びます。「どの段階で失敗したか」を切り分ける最初の一歩として有効です。

サーバーを運用する側では、SSH の設定を締めることが基本的なセキュリティ対策になります。具体的には、パスワード認証を無効化して公開鍵認証のみにする、root の直接ログインを禁止する、といった設定です。最近の OpenSSH の変更点は OpenSSH 10.4 の新機能とセキュリティ修正でも扱っています。なお、SSH の上でファイル転送を行う SFTP を lftp から使う手順は lftp で SFTP サーバーに接続する方法にまとめています。

まとめ

  • SSH-2 はトランスポート層(RFC 4253)・認証層(RFC 4252)・接続層(RFC 4254)の三層構成。既定ポートは TCP 22番
  • 接続は「TCP 3ウェイハンドシェイク -> バージョン交換 -> KEXINIT でアルゴリズム合意 -> 鍵交換とホスト鍵検証 -> ユーザー認証 -> チャネル」の順に進む
  • ホスト鍵の検証は known_hosts による TOFU が基本。RFC 4251 も初回接続を弱点と認めており、初回フィンガープリントは別経路で照合するのが安全
  • 公開鍵認証では秘密鍵は流れず、セッション識別子を含むデータへの署名だけを送る。許可鍵は authorized_keys で管理する
  • ポートフォワーディングは接続プロトコルのチャネル多重化の応用。-L(ローカル)、-R(リモート)、-D(SOCKS)を使い分ける
  • 実務では ~/.ssh/config と ssh-agent で運用を楽にし、ssh -v で失敗箇所を切り分ける

SSH は「鍵さえあればつながる」道具ですが、その裏では鍵交換・ホスト鍵検証・署名という複数の仕組みが噛み合って安全を支えています。各段階が何を保証しているのかが分かると、接続エラーの調査もサーバー側のセキュリティ設定も、迷わず勘所を押さえられるようになります。

参考リンク

OpenSSH 10.4 の新機能とセキュリティ修正 - sftp/scp の脆弱性対応と量子耐性への流れ

OpenSSH 10.4 の新機能とセキュリティ修正 - sftp/scp の脆弱性対応と量子耐性への流れ

11

2026年7月6日にリリースされた OpenSSH 10.4 を一次情報で整理します。悪意あるサーバによる sftp ダウンロード先のすり替え、scp のリモート間コピーでの親ディレクトリ書き込み、鍵再交換時のクライアント側 use-after-free など複数のセキュリティ修正の中身に加え、ML-DSA 44 と Ed25519 を組み合わせた実験的なポスト量子署名の追加、そして mlkem768x25519 デフォルト化・DSA 削除といった量子耐性への流れまで、実務での確認ポイントとともにまとめます。

DNS の仕組み入門 - 名前解決・レコード種別・TTL とキャッシュを理解する

DNS の仕組み入門 - 名前解決・レコード種別・TTL とキャッシュを理解する

23

DNS(ドメインネームシステム)の名前解決の仕組みを、一次ソースである RFC を軸に整理します。ルート・TLD・権威サーバーの階層、再帰リゾルバとスタブリゾルバ、A/AAAA/CNAME/MX/NS/SOA/TXT/CAA/SRV/PTR などのレコード種別、再帰と反復の解決の流れ、TTL とキャッシュ、ネガティブキャッシュ(RFC 2308)、ポート53と UDP/TCP・EDNS0(RFC 6891)、DNSSEC(RFC 4033)、DoH(RFC 8484)/DoT(RFC 7858)まで、Web 開発者・インフラ担当者向けにまとめます。

ロードバランシングのアルゴリズム入門 - ラウンドロビンから最少コネクション・P2Cまで

ロードバランシングのアルゴリズム入門 - ラウンドロビンから最少コネクション・P2Cまで

14

負荷分散(ロードバランシング)の基礎を実務目線で整理します。L4とL7の違い、ラウンドロビン・加重ラウンドロビン・最少コネクション・最短応答時間・IPハッシュ・Power of Two Choices・Maglevといった主要アルゴリズムの挙動と向き不向き、nginx/HAProxy/Envoyの既定、ヘルスチェックやスティッキーセッションまで、公式ドキュメントを出典にまとめます。