SSHのStrictHostKeyChecking yes・accept-new・ask・noの違い:known_hostsは新しい鍵と変わった鍵をどう扱うか
OpenSSHクライアントが初めて見るホスト鍵と変わったホスト鍵をStrictHostKeyCheckingの値ごとにどう扱うか、known_hostsの行の形式、ssh-keygen -F・-R・-Hの使い方をOpenBSDのマニュアルとRFC 4251に基づいて整理しました。
SSHで初めて接続するサーバーについて、クライアントはまだそのホスト鍵を知らない。以前接続したことのあるサーバーが、前回とは違う鍵を提示してくることもある。OpenSSHクライアントがこの2つの場合をどう扱うかは StrictHostKeyChecking オプションで決まり、照合に使う鍵は known_hosts ファイルに入っている。自動化スクリプトに StrictHostKeyChecking=no を書く前に、それぞれの値が何を許すのかを正確に見ておきたい。
この記事は、OpenBSDのマニュアルサーバーにあるOpenBSD-currentのマニュアル ssh_config(5)(ページの日付は2026年10月9日)、sshd(8)(2026年9月2日)、ssh-keygen(1)(2026年10月1日)と、SSHプロトコルのアーキテクチャを定めた RFC 4251 に基づいていて、いずれも2026-10-19に確認した。LinuxディストリビューションやmacOSに入っているOpenSSHはバージョンが異なり、既定値やオプションが違う可能性があるので、手元の man ssh_config で確かめてほしい。OpenSSH以外のSSH実装は扱わない。警告メッセージの実際の文言やホスト鍵の検証手順を詳しく説明しているssh(1)のマニュアルは今回の根拠に使っていないため、この記事では引用しない。
ホスト鍵の検査が防ぐもの
RFC 4251 §4.1によると、サーバーのホスト鍵は鍵交換の中で、クライアントが本当に正しいサーバーと通信しているかを確かめるために使われる。そのためにはクライアントがサーバーの公開ホスト鍵を前もって知っている必要がある。RFCは2つの信頼モデルを挙げている。
- クライアントが、ホスト名(ユーザーが入力した名前)と公開ホスト鍵を対応づけたローカルデータベースを持つ。中央の基盤は要らないが、管理が負担になりうる。
- 信頼する認証局(CA)が名前と鍵の対応を証明する。クライアントはCAのルート鍵だけを知っていればよい。
OpenSSHの known_hosts は1つ目のモデルのデータベースにあたる。後で触れる @cert-authority マーカーは、2つ目のモデルを同じファイルの中で使うための仕組みだ。
同じ節には、初回接続時に名前と鍵の対応を検査しない選択肢がプロトコルにあると書かれている。この場合も受動的な盗聴は防げるが、能動的な中間者(MITM)攻撃には弱くなるため、実装はこうした接続を通常は既定で許すべきではない(SHOULD NOT)。代わりに、初回だけ検査せずに鍵を受け入れてローカルデータベースに保存し、以後の接続ではその鍵と比較するという戦略を例として挙げている。また、検証できないホスト鍵を受け入れないオプションをすべての実装が用意すべきだ(SHOULD)としている。
§9.3.4は、鍵が事前に安全に配布されていれば中間者が提示した鍵は保存済みの鍵と一致せず、そのときユーザーに警告すべきだ(SHOULD)と説明している。ホストとその鍵の結びつきを信頼できる形で確かめずにプロトコルを使うことは本質的に安全ではなく、推奨されない(NOT RECOMMENDED)というのがこの節の結論だ。
鍵はどこから探されるか
ssh_config(5)によると、クライアントは2種類のファイルを読む。
| オプション | 既定値 | 意味 |
|---|---|---|
UserKnownHostsFile |
~/.ssh/known_hosts、~/.ssh/known_hosts2 |
ユーザーごとのホスト鍵データベース。none にするとユーザーごとのファイルを無視する。 |
GlobalKnownHostsFile |
/etc/ssh/ssh_known_hosts、/etc/ssh/ssh_known_hosts2 |
システム全体のホスト鍵データベース |
sshd(8)のSSH_KNOWN_HOSTS FILE FORMATの節は、グローバルなファイルは管理者があらかじめ用意するもの(任意)で、ユーザーごとのファイルは自動で管理され、未知のホストに接続するたびにその鍵が追加されると説明している。ただし、クライアントが実際に鍵を自動で追加するかどうかはクライアントのポリシーと設定による。後の StrictHostKeyChecking の表のとおり、yes では自動で追加されず、ask ではユーザーが確認したあとでだけ追加される。2つのファイルの内容が食い違うこともあるが、どちらかで有効な情報が見つかれば認証は受け入れられる。KnownHostsCommand を指定すると、これらのファイルを読んだあとにコマンドを実行し、同じ形式のホスト鍵の行を追加で得る。このコマンドが異常終了するか0以外の終了ステータスを返すと、接続は切断される。
StrictHostKeyCheckingの値ごとの動作
ssh_config(5)の説明を、新しいホスト(鍵がまだない場合)と変わった鍵(保存済みの鍵と違う場合)に分けて整理するとこうなる。
| 値 | 新しいホスト鍵 | 変わったホスト鍵 |
|---|---|---|
yes |
自動では追加しない。新しいホストはすべてユーザーが手で追加する。 | 接続を拒否する。 |
accept-new |
ユーザーの known_hosts ファイルに自動で追加する。 |
接続を許可しない。 |
ask(既定値) |
ユーザーが本当に望むか確認したあとでだけ追加する。 | 接続を拒否する。 |
no または off |
ユーザーのknown hostsファイルに自動で追加する。 | 「いくつかの制限のもとで」接続の続行を許す。 |
マニュアルは、どの値でも既知のホストの鍵は自動的に検証されると付け加えている。つまりこのオプションが変えるのは鍵がない、または違うときの扱いで、保存済みの鍵との照合そのものを止めるわけではない。
yes はマニュアルの言葉で言えばMITM攻撃に対して最大限の保護になるが、/etc/ssh/ssh_known_hosts がきちんと管理されていない環境や、新しいホストに頻繁に接続する環境では面倒になりうる。
no の「いくつかの制限」が具体的に何なのかはssh_config(5)には書かれていない。その内容を扱うssh(1)はこの記事の根拠に使っていないので、制限の中身は説明しない。上の表から確かに言えるのは、4つの値のうち鍵が変わったときに接続を拒否しないのは no(off)だけだということだ。
どの値を選ぶか
この節はマニュアルとRFCの内容から導いた判断だ。
accept-newは、RFC 4251 §4.1が例に挙げた「初回は受け入れて保存し、以後は比較する」戦略と同じ形をしている。初回接続の経路が安全だという前提を受け入れられる自動化処理なら、対話的な確認なしで動きつつ、変わった鍵は止められる。ただし初回接続で受け取った鍵が本物かどうかは確かめていない。yesは鍵を事前に配布できるときに使う。sshd(8)はknown_hostsの行を手で打ち込まず、スクリプトやssh-keyscan(1)で生成するか、サーバーの/etc/ssh/ssh_host_*_key.pubの内容の先頭にホスト名を付けて作るよう勧めている。ネットワーク越しに鍵を取ってくる方法は、取得した鍵を別の手段で確かめない限り初回接続と同じ問題を抱える。RFC 4251 §9.3.4は、サーバー管理者がホスト鍵そのものの完全性に頼らない手段(安全なWebページ、紙など)でフィンガープリントを確認できるようにすることを勧めている。noは、ホスト鍵が変わったときに接続を一律には拒否しない唯一の値だ。接続は「いくつかの制限のもとで」続行されるだけで、ssh_config(5)は変わった鍵を受け入れる、あるいは保存するとは書いていない。それでも、RFC 4251 §9.3.4が推奨しないとした使い方にかなり近い。
known_hostsの1行の形式
sshd(8)のSSH_KNOWN_HOSTS FILE FORMATの節によると、各行には空白で区切られた次のフィールドが入りうる。マーカー(任意)、ホスト名、鍵の種類、base64でエンコードした鍵、コメント(任意)だ。マーカーとコメントは任意なので、下の例にはフィールドが3つや4つしかない行もある。コメントは行末まで続き、使われない。# で始まる行と空行は無視される。
以下はsshd(8)に載っている例のファイルを行ごとに分けたものだ(鍵の値は原文でも省略されている)。
# Comments allowed at start of line
cvs.example.net,192.0.2.10 ssh-rsa AAAA1234.....=
# A hashed hostname
|1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa AAAA1234.....=
# A revoked key
@revoked * ssh-rsa AAAAB5W...
# A CA key, accepted for any host in *.mydomain.com or *.mydomain.org
@cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W...
ホスト名フィールドの規則は次のとおり。
- カンマ区切りのパターンのリストで、
*と?はワイルドカードになる。パターンの前に!を付けると否定になり、名前が否定パターンに一致すると、同じ行の別のパターンに一致していてもその行では受け入れられない。 - 標準以外のポートは、名前やアドレスを角括弧で囲み、コロンとポート番号を付けて
[ホスト]:ポートと書く。 - ハッシュ化された名前は
|で始まる。1行に置けるハッシュ化された名前は1つだけで、否定やワイルドカードは使えない。
クライアントがサーバーを認証するとき、これらのパターンと照合される名前はユーザーが指定したホスト名だ。HostKeyAlias を指定していればその値が、CanonicalizeHostname オプションを使っていれば正規化されたサーバー名が代わりに使われる。ssh_config(5)は、HostKeyAlias はSSH接続をトンネルするときや、1つのホストで複数のサーバーを動かすときに役立つと説明している。
マーカーは2種類ある。@cert-authority はその行の鍵がCAの鍵であることを示す。サーバーが証明書を提示した場合、その証明書に署名したCAの鍵がこのマーカー付きの行にあれば認証が受け入れられる。@revoked は失効した鍵で、認証にもCAとしても決して受け入れられず、見つかるとssh(1)が警告を出す。同じ名前に複数の行や異なる鍵を置くことは許されているが、推奨はされていない。
CheckHostIP を yes にすると、クライアントは known_hosts でホストのIPアドレスも追加で検査する。マニュアルによると、これはDNSスプーフィングでホスト鍵が変わった場合を検出するためのもので、その過程で StrictHostKeyChecking の設定に関係なく接続先のアドレスを ~/.ssh/known_hosts に追加する。現在の既定値は no だ。
ssh-keygenでknown_hostsを扱う
ssh-keygen(1)には known_hosts を編集するオプションが3つある。以下のコマンドは使い方を示すためのもので、この記事を書く際に実行はしていない。server.example.net は例として使う名前だ。
# 指定したホストの行を探して表示する(ハッシュ化された名前も見つかる)
ssh-keygen -F server.example.net -f ~/.ssh/known_hosts
# 指定したホストの鍵をすべて削除する。標準以外のポートは [名前]:ポート で書く
ssh-keygen -R '[server.example.net]:2222' -f ~/.ssh/known_hosts
# ファイル内のホスト名とアドレスをすべてハッシュに置き換える
ssh-keygen -H -f ~/.ssh/known_hosts
-Fは指定したホスト名(ポート付きも可)をknown_hostsファイルから探し、見つかったものをすべて表示する。ハッシュ化された名前を探すのに便利だ。-Rは指定したホスト名(ポート付きも可)に属する鍵をすべて削除する。ハッシュ化されたホストを消すのに便利だ。-Hはすべてのホスト名とアドレスをハッシュに置き換え、元の内容は.oldという接尾辞の付いたファイルに移す。すでにハッシュ化された名前には手を付けないので、両方の形式が混ざったファイルにも安全に使える。
例で -f を明示しているのは、確認したマニュアルの本文に -f を省略したときにどのファイルが使われるかが書かれていないからだ。-H のあとに残る .old ファイルにはハッシュ化する前の名前がそのまま入っていることも覚えておきたい。名前を隠すためにハッシュ化したのなら、そのファイルも処理する必要があるというのは上の説明から導いた結論だ。
新しく追加される行を最初からハッシュ化したいなら、ssh_config(5)の HashKnownHosts を有効にする。既定値は no で、有効にしてもファイルにすでにある名前は自動では変換されないので、それには別途 ssh-keygen -H が要る。
変わった鍵に出会ったとき
サーバーを再インストールした、鍵を交換したといった確かな理由があっても、-R で行を消して再接続した時点で、新しい鍵はまた「初めて見る鍵」になる。RFC 4251 §9.3.4の勧めに従い、新しいフィンガープリントをホスト鍵そのものとは無関係な経路で確かめてから受け入れるのが順序だ。この順序はRFCの勧告を実際の手順に当てはめたもので、マニュアルが定めた手順ではない。
鍵の交換に使うUpdateHostKeys
サーバー側で鍵を交換する予定があるなら、ssh_config(5)の UpdateHostKeys が関係する。これを有効にすると、クライアントは認証完了後にサーバーが送ってくる追加のホスト鍵の通知を受け入れ、UserKnownHostsFile に加える。サーバーは古い鍵を削除する前に新しい公開鍵を送れるので、無理なく鍵を交換できる。
追加の鍵が受け入れられるのは、次の条件をすべて満たすときだけだ。
- ホストの認証に使われた鍵が、すでに信頼されているか、ユーザーが明示的に受け入れたものである。
- ホストが
GlobalKnownHostsFileではなくUserKnownHostsFileで認証された。 - 証明書ではなく通常の鍵で認証された。
ユーザーが UserKnownHostsFile の既定値を変えておらず、VerifyHostKeyDNS も有効にしていなければ UpdateHostKeys は既定で有効になり、そうでなければ no になる。ask にすると known_hosts を変更する前に確認を求められるが、この確認は現在 ControlPersist と両立せず、その場合は無効になる。マニュアルによると、この機能が使う “hostkeys@openssh.com” 拡張に対応しているのは、現時点ではOpenSSH 6.8以降のsshd(8)だけだ。
確認すること
- 設定ファイルやスクリプトで
StrictHostKeyChecking no(またはoff)を探す。自動化が目的ならaccept-newで足りるか検討する。 - 初回接続の鍵を何で確かめるかを決める。鍵を事前に配布できるなら、
yesとグローバルまたはユーザーのknown_hostsを組み合わせる。 - 標準以外のポートのサーバーは
[名前]:ポートの形で保存・削除されることを、-Fや-Rを使うときに考慮する。 ssh-keygen -Hを実行したなら、.oldファイルをどうするか決める。- この記事の既定値(
ask、HashKnownHosts no、CheckHostIP no、UpdateHostKeysの条件)が、実際に使っているOpenSSHのマニュアルと同じか確かめる。
参考資料
いずれも2026-10-19に確認した。マニュアルはOpenBSD-current版。
- ssh_config(5), OpenBSD manual pages(ページの日付は2026年10月9日): CheckHostIP、GlobalKnownHostsFile、HashKnownHosts、HostKeyAlias、KnownHostsCommand、StrictHostKeyChecking、UpdateHostKeys、UserKnownHostsFile、VerifyHostKeyDNS
- sshd(8), OpenBSD manual pages(ページの日付は2026年9月2日): SSH_KNOWN_HOSTS FILE FORMAT、FILES
- ssh-keygen(1), OpenBSD manual pages(ページの日付は2026年10月1日): -F、-H、-R
- RFC 4251: The Secure Shell (SSH) Protocol Architecture(2006): §4.1、§9.3.4