第3章 OpenSSH のしくみ

Last Modified: Tue Sep 21 11:15:17 UTC 2010

この章では OpenSSH の簡単なしくみとその動作を説明します。

OpenSSH はクライアント ("ssh" コマンド) と サーバデーモン ("sshd" プログラム) の組み合わせを基本とします。 それぞれ「使う人」と「使われ方」につぎのような差異があります (図 openssh-overview)。

  1. 管理者が sshd デーモンをインストールおよび設定し、クライアントのログインを待ち受ける。
  2. 一般ユーザが ssh コマンド (クライアント) をつかってログインし、 シェルやファイル転送、ポート転送、VPN などの機能を利用する。
この章では 1. のインストールと設定が完了したあとに、 ユーザが ssh コマンドを使ってログインするまでの基本的な流れを説明します。

図 openssh-overview. ssh クライアントと sshd サーバ

一般のユーザが OpenSSH の詳細を理解している必要はありません。 しかし OpenSSH の動きはその原理と大きく関係しているため、 システム管理者は、たとえ大まかにでも内部の動きを理解しておく必要があります。 この知識はいずれ OpenSSH にトラブルが発生したとき、原因究明をするうえでも役に立ちます。 この知識は、将来発生するかもしれないトラブルの原因を究明するうえでも役に立ちます。 そこで、実際のログイン手順を紹介する前に、一般ユーザがクライアントをつかい、 サーバデーモンに対してログインを試みたとき、OpenSSH の内部で 何が起こるかを見ていきたいと思います。

3.1. ユーザがログインするまでの流れ

一般ユーザがクライアントを用いてサーバにログインを試みると、 クライアントとサーバデーモンのあいだで 2 種類の認証が交わされます:

  1. そのサーバが本当に「ユーザのログインしたいサーバ」であるかどうかを確認する、ホスト認証
  2. そのユーザがサーバにログインできるかどうかを確認する、ユーザ認証

この 2種類の認証は、安全な通信を約束するために必要不可欠なものです。 以下、これらの認証が必要な理由を順を追って説明していきましょう。

3.2. ホスト認証のしくみ

3.2.1. ホスト認証はなぜ必要か

最初に、クライアントもサーバもまったく暗号を使わない場合を考えてみましょう。 IPアドレス aa.bb.cc.dd をもつクライアントが、 IPアドレス xx.yy.zz.ww をもつサーバにログインするとします。 ご存知のとおり、暗号をまったく使わない場合、 素のデータがそのままネットワーク上を流れるため、 第三者によって簡単に盗み聞きされてしまいます (図 eavesdropping)。


図 eavesdropping. 暗号を使わない場合

これを防ぐために、クライアントとサーバ間の通信を暗号化したとします。 これによって第三者の盗み聞きは不可能になり、一見、問題は解決したかにみえます (図 crypto-prevents-eavesdropping)。


図 crypto-prevents-eavesdropping. 暗号を使った場合

3.2.2. なりすましによるパスワードの盗難

ところが、この第三者も暗号を使うとしたらどうでしょうか。 第三者はサーバと同じ IP アドレス xx.yy.zz.ww を名乗り、 クライアントから受けとった暗号化されたデータを一回、元の状態に復元し、 そのうえでふたたび暗号化して本物のサーバに送信したとします。 このとき、クライアントとサーバのどちらも通信が盗み聞きされたとは気づきません。 第三者は中継するデータを改ざんし、クライアントに「なりすます」こともできます (図 mitm-attack)。


図 mitm-attack. なりすまし攻撃 (Man in the middle)

実際には、ここまで大がかりな仕掛けをする犯罪者はそうそういません。 しかし、偽のサーバを仕掛けておいて相手の入力したパスワードを収集し、 その後「パスワードが間違っている」といって接続を切ってしまうという攻撃は、 より実際にありがちなものです (図 mitm-steal-passwords)。


図 mitm-steal-passwords. パスワードを盗む第三者

この場合、だまされたユーザは以下のような体験をすることになります。

$ ssh yusuke@dontcare.example.com         (dontcare.example.com にログインを試みる)
The authenticity of host 'dontcare.example.com (xx.xx.xx.xx)' can't be established. (このホストが正しいか確証がないと言ってきた)
RSA key fingerprint is fe:fe:fe:fe:af:bb:fe:fe:fe:fe:3e:fe:cc:fe:fe:ff.
Are you sure you want to continue connecting (yes/no)? yes    (適当に yes と答える)
Warning: Permanently added 'dontcare.example.com,xx.xx.xx.xx (RSA) to the list of known hosts.
yusuke@dontcare.example.com's password:  (正しいパスワードを入力する)
Permission denied, please try again.     (ログインできない)
yusuke@dontcare.example.com's password:  (正しいパスワードを入力する)
Permission denied, please try again.     (ログインできない)
yusuke@dontcare.example.com's password:  (正しいパスワードを入力する)
Permission denied (publickey,password).  (ログインできない)
(相手にパスワードが知られてしまった)

この例で、OpenSSH クライアントが "Are you sure you want to continue connecting (yes/no)?" (このホストへの接続を本当に続けるか?) と尋いてきていることに注目してください。 これはクライアントが、本当にその IP アドレスが目的のホストに到達しているかわからない、 と教えてくれているのです。ここで、もし素性がはっきりしないホストに接続してしまうと、 上の例のように正しいパスワードを第三者に知らせてしまうことになりかねません。

3.2.3. なりすましを防ぐしくみ

通常はこのようなことが起こらないよう、 クライアントはサーバに接続した瞬間に、まず暗号化された通信を介して そのサーバのホスト鍵 (host key) を確認し、 それが本当に自分のログインしたいサーバであるかどうか確かめます。 ホスト鍵はホスト公開鍵とホスト秘密鍵に分かれており、 クライアント上には通常 known_hosts と呼ばれるファイルがあり、 ここには特定の IPアドレス (とホスト名) をもつサーバのホスト公開鍵が登録されています。 ホスト秘密鍵はサーバマシン内のディスクに格納されており、 ネットワーク上に持ち出されることはありません。 クライアントは、まずこの known_hosts ファイル内に登録されているホスト公開鍵と、 サーバから送られてくるホスト公開鍵を照合し (図 what-is-host-authentication)、 サーバが実際にこのホスト公開鍵に対応するホスト秘密鍵をもっているかどうか確認します。 この確認には公開鍵暗号技術が使われており、 サーバは実際のホスト秘密鍵をネットワーク上に送信することなく、ホスト秘密鍵の所有を クライアント側に証明できるようになっています (コラム - 公開鍵をつかった認証のしくみ 参照)。


図 what-is-host-authentication. ホスト認証
コラム - 公開鍵をつかった認証のしくみ

サーバがホスト秘密鍵を実際にネットワーク上に送信しないにもかかわらず その所有を証明できるのは、秘密鍵と公開鍵の間にある数学的な性質のためです。 秘密鍵と公開鍵は「公開鍵暗号」と呼ばれる暗号技術の一種をもとに設計されており、 たとえば RSA 公開鍵暗号は以下のような特徴をもっています。

  • 秘密鍵で暗号化されたデータは、それに対応する公開鍵によってのみ正しく復号できる。
  • 公開鍵で暗号化されたデータは、それに対応する秘密鍵によってのみ正しく復号できる。

例として、ある人物 A が別の人物 B に対して秘密鍵の所有を証明したいとしましょう。 B はあらかじめこの秘密鍵に対応する公開鍵を持っているとします。 RSA 公開鍵暗号を使った認証では、B はまず誰にも知られないように 乱数 N を生成し、これを A の公開鍵で暗号化して A に送り、 元の値を当てるよう要求します (チャレンジ)。N は B 以外に誰も知らないので、 暗号化されたデータから元の値 N を得るには、 A は秘密鍵をつかって復号するしかありません。したがって、 もし A が送り返してきた値 (レスポンス) が元の N と同じならば、 おそらく A は秘密鍵を所持しているだろうと推測できます (図 challenge-response)。 しかし B は A の秘密鍵の内容については何も知ることはできません。 このような証明方法は、相手に何の知識も与えずに ただその存在だけを証明するという意味で「ゼロ知識証明」と呼ばれています。 より詳しい内容に興味がある方は、参考文献を参照してください。


図 challenge-response. 公開鍵をつかった認証

ホスト認証は通常 (最初の 1回目を除いて) クライアントによって自動的に行われます。 ホスト認証を行うためには、クライアントはログインする先のホスト公開鍵をあらかじめ知っている必要があります。 ところが、あるホストにはじめてログインする場合、そのホスト公開鍵はまだ known_hostsファイルには登録されていません (図 host-authentication-firsttime)。 そのため、クライアントはサーバが送ってきたホスト公開鍵の指紋 (fingerprint) をユーザに提示し、 これが本当にユーザの接続したいサーバのものであるかどうか確認を求めるのです。 一般的にあるデータの「指紋」とは、そのデータの特徴をあらわすように 作られた短い文字列や数値のことをさします。もしホスト公開鍵が何者かによって改ざんされていれば、 その指紋は違った値になるはずなので、改ざんが判明します。 なお OpenSSH で「鍵の指紋」という場合、それはすべて公開鍵の指紋を意味します。 [脚注: 通常、指紋の生成には一方向ハッシュ関数が使われており、指紋をまったく変えないように データ内容だけを変更することはきわめて難しいと考えられています。]


図 host-authentication-firsttime. あるホストにはじめてログインする場合

結論から言うと、ユーザがこの質問に答えるためには ネットワーク以外の通信方法を使わなければなりません。 セキュリティについて本当に心配している場合は、本来このときサーバ側の管理者に (電話などで) 問い合わせて、この指紋が正しいかどうかを確認する必要があります。 [脚注: しかし、実際にはここまでしているユーザはあまりいないのが現状です。] たとえば、ユーザはサーバ dontcare.example.com のホスト公開鍵の指紋が "ac:62:6e:32:7e:6f:bc:6a:2f:a1:e5:f3:a5:db:7f:c0" であることを前もって知っていたとしましょう。その場合、 dontcare.example.com への第1回目のログイン時には、 以下のようなメッセージが表示されることになります。

正常な状態:

$ ssh yusuke@dontcare.example.com
The authenticity of host 'dontcare.example.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is ac:62:6e:32:7e:6f:bc:6a:2f:a1:e5:f3:a5:db:7f:c0.  (ホスト公開鍵の指紋が一致している)
Are you sure you want to continue connecting (yes/no)? yes     (接続を続行する)
Warning: Permanently added 'dontcare.example.com,xx.xx.xx.xx' (RSA) to the list of known hosts. (ホスト公開鍵がデータベースに記録された)

ここでホスト公開鍵の指紋が正しければ yes と答えます。 するとクライアントはこのホスト公開鍵を known_hosts ファイルに サーバの名前 dontcare.example.com とあわせて記録し、2回目以降の照合に使います。 [脚注: したがって、サーバの sshd をアップデートするさい、 以前と同じホスト鍵をそのまま使用することは非常に重要です。 ホスト鍵を変えてしまうと、ユーザの目には何者かがそのホストになりすましているように 見えてしまいます。] いっぽう、何者かがそのサーバになりましている場合、 その第三者はサーバの IP アドレスを詐称することができても、ホスト秘密鍵までは詐称できません。 したがってそのマシンが送ってきたホスト公開鍵を見れば、なりすましが判別できます。

ホスト鍵が変えられている場合:

$ ssh yusuke@dontcare.example.com
The authenticity of host 'dontcare.example.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is fe:fe:fe:fe:af:bb:fe:fe:fe:fe:3e:fe:cc:fe:fe:ff.  (ホスト公開鍵の指紋が違う)
Are you sure you want to continue connecting (yes/no)? no (接続を切る)

このような場合は必ず no を答えて接続を切り、 サーバの管理者あるいはネットワーク管理者に連絡してください。 さもないと、ユーザはうっかり秘密のパスワードを偽装されたサーバに漏らしてしまうことになりかねません。

後述する公開鍵認証を使うと、この必要性は (完全とはいわないまでも) 減らすことができます。 なぜなら公開鍵認証ではユーザが入力したパスワード (正確にはパスフレーズ) は いっさいネットワーク上に流れないため、パスワードを盗まれる心配はないからです (もっともそのユーザが本当に第三者の用意した偽のサーバにログインした場合、 それによってべつの情報が漏れる危険性はあります)。

2回目以降のログインでは、 クライアントはサーバが送ってきたホスト公開鍵を known_hosts ファイルに 登録されているものと照合します。ホスト公開鍵が登録されているものと同じであれば、 クライアントは第1回目のような質問をユーザに尋ねることはありません。

正常な状態:

$ ssh yusuke@dontcare.example.com
yusuke@dontcare.example.com's password:  (パスワードを入力する)

いっぽう、2回目以降のログインで何者かがなりすましをした場合、 クライアントは偽のサーバが送ってきたホスト公開鍵が known_hosts ファイルに登録されているものと異なっていることを検出し、 ユーザに警告を発します (図 wrong-host-key)。この場合も公開鍵認証を おこなうことはできますが、パスワードの入力は禁止されます。


図 wrong-host-key. ホスト鍵が変えられている場合

ホスト鍵が変えられている場合:

$ ssh yusuke@dontcare.example.com
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @              (ホスト鍵が変わっている旨の警告)
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!                    (なりすまし攻撃の危険がある)
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.         (ホスト鍵が本当に変えられた可能性もある)
The fingerprint for the RSA key sent by the remote host is
fe:fe:fe:fe:af:bb:fe:fe:fe:fe:3e:fe:cc:fe:fe:ff.                         (管理者に連絡して、ホスト鍵の指紋を確認すること)
Please contact your system administrator.
Add correct host key in /home/yusuke/.ssh/known_hosts to get rid of this message.
Offending key in /home/yusuke/.ssh/known_hosts:24                        (known_hosts ファイルの 24行目にある鍵と矛盾する)
Password authentication is disabled to avoid man-in-the-middle attacks.  (パスワードの入力は禁止される)
Keyboard-interactive authentication is disabled to avoid man-in-the-middle attacks.

3.3. ユーザ認証のしくみ

クライアントが正しいサーバを確認したら、つぎはサーバのほうが 正しいユーザかどうかを確認する番です。これをユーザ認証といいます。 OpenSSH のユーザ認証には大きく分けて 2つの方式があります:

  1. ユーザがあらかじめ登録したパスワードを使った認証 (パスワード認証)
  2. ユーザがあらかじめ登録した秘密鍵・公開鍵ペアを使った認証 (公開鍵認証)

どちらもユーザがある特定の (そのユーザにしか知りえない) 情報をもっているかどうかを 確認することによって、そのユーザがログインできるかどうかを判断しています。 なお、OpenSSH ではここで挙げたパスワード認証と公開鍵認証のほかに、 Hostbased認証と呼ばれるユーザ認証の方式がサポートされています。 Hostbased認証については 6.6. Hostbased 認証 を使う を参照してください。

3.3.1. パスワードを使ったユーザ認証

おそらく、ほとんどの人がよく知っているのはパスワード認証でしょう。 これは通常の UNIX のログインに使うのと同じパスワードをユーザが入力し、 それによって本人かどうかを確認するものです。 OpenSSH ではユーザの入力したパスワードは暗号化されますから、 ホスト認証が正しく完了していれば安全にログインできます。

しかし、本書では 3.3.2. 秘密鍵・公開鍵ペアを使ったユーザ認証 で説明する公開鍵認証を使うことをおすすめします。 パスワード認証では、たとえ暗号化されているとはいえユーザはパスワードを相手のマシンに 教えなければならないので、ユーザは 3.2.2. なりすましによるパスワードの盗難 のように第一回目のログインで うっかり第三者にパスワードをもらしてしまう危険性があります。 また、最近では世界中の SSH サーバに対してパスワードをしらみつぶしに推測する攻撃が 発生しているので、推測されやすい (弱い) パスワードを使っているユーザは アカウントをのっとられる危険性もあります。

3.3.2. 秘密鍵・公開鍵ペアを使ったユーザ認証

いっぽう、パスワード認証のかわりに普及してきているのが公開鍵認証です。 パスワード認証と違う点は、

この場合、サーバへのログインは次のようにして行われます (図 pubkey-authentication):

  1. クライアントがサーバに接続する。
  2. クライアントは、自分がある公開鍵に対応する秘密鍵を持っていることを宣言する。
  3. サーバはその公開鍵が、あらかじめそのユーザに対して登録されたものであるかどうかを確認し、 もしそうならば、対応する秘密鍵の証明を求める (チャレンジ)。
  4. クライアントが秘密鍵の所有を数学的に証明する (レスポンス)。 このとき、ユーザは秘密鍵を使用するためにパスフレーズを入力するが、 このパスフレーズはネットワーク上に流れない。
  5. 正しいレスポンスが送られてくると、 サーバはユーザが秘密鍵を持っていることを確信し、ログインを許可する。

図 pubkey-authentication. 公開鍵認証

公開鍵認証の特徴は、パスワードや秘密鍵などの 「一度知られたら何度でも自分のかわりにログインされてしまう情報」が いっさいネットワーク上に流れないことです。もっとも、この場合もユーザは パスワードに相当するパスフレーズをキーボードから入力する 必要がありますが、この情報はクライアント上で秘密鍵を使用するときにだけ使われ、 ネットワーク上に送信されることはありません。秘密鍵は通常、暗号化されてディスク上に格納されており、 ユーザ認証のときのみパスフレーズによって復元されます。

すべてのユーザが公開鍵認証を使うようになればパスワード認証を禁止でき、 その結果セキュリティは飛躍的に向上します。もっとも、この場合も 「なりすまし」を防ぐことはできませんが、たとえ「なりすまし」が行われた場合でも、 パスワードが盗まれるということはありません。また公開鍵認証を使うと 次項で説明する認証エージェントが使えるため、ユーザにとっても便利です。

表 pubkey-pros に公開鍵認証を使うメリットを挙げておきます。
表 pubkey-pros. 公開鍵認証を使うメリット
  • 認証エージェントが使える。(4.4. 認証エージェントを使う)
  • ユーザごとの動作をより細かくコントロールできる。(5.4. ユーザの操作を制限する)
  • パスワードの推測攻撃に対して安全。
  • アカウントをつくるとき、ユーザが管理者に (あるいは、管理者がユーザに) パスワードを教える必要はない。
    したがって、遠隔地から安全にアカウントが発行できる。

3.3.3. 認証エージェントを使った認証

OpenSSH では上の 2つの認証方式に加えて 「認証エージェント (authentication agent)」 によるユーザ認証をサポートしています。認証エージェントとは ユーザにかわって認証を行ってくれるプログラムのことで、 これは OpenSSH クライアントと通信して公開鍵認証を行います。 このさい、クライアントは認証エージェントとサーバの間で 暗号化された通信を自動的に中継します (図 what-is-authentication-agent)。


図 what-is-authentication-agent. 認証エージェントを使ったユーザ認証

通常の公開鍵認証では、ユーザのパスフレーズによって 復元された秘密鍵は一回しか認証に使われず、一度ログインしたあとは失われてしまいます。 これに対して認証エージェントは復元された秘密鍵を一定時間にわたってメモリ上に保持し、 何度も使うことができます。認証エージェントを使うと、ユーザはパスフレーズを 最初に一度入力するだけでよく、何度もパスフレーズを入力するわずらわしさから解放されます。 ただし、この便利さは諸刃の剣にもなります。ユーザが認証エージェントに復元された鍵を 保持させたままで放置しておくと、 秘密鍵のパスフレーズを知らない第三者でもサーバにログインできてしまうからです。 OpenSSH では、認証エージェントが 復元された秘密鍵をディスクに保存せず、一定時間が経過すると自動的に復元された秘密鍵を メモリ上から消去することによって、この危険性を抑えています。

このように便利な認証エージェントですが、 ひとつ大きな欠点があります。それは動作が複雑なため、 理解しにくく、使うのも面倒だということです。 そのため、UNIX に不慣れな一般ユーザが認証エージェントを 使いこなせるかどうかはシステム管理者の支援にかかっています。 4.4. 認証エージェントを使う では認証エージェントの使い方としくみを詳しく解説し、 5.1.3. 認証エージェントの使用を支援する では一般ユーザが簡単に認証エージェントを使えるようにする ための設定について紹介します。
コラム - Diffie-Hellman 鍵交換とは

暗号を使って通信するには、お互いが暗号をどのように解くかを知っていなければなりません。 OpenSSH では、これは暗号化方式と、暗号化に使う「鍵」に依存しています。 しかし、そもそも第三者が盗聴しているかもしれない状況で、 いったいどうやって暗号鍵を秘密裏に相手に伝えればよいのでしょうか? Diffie-Hellman 鍵交換 (DH鍵交換) アルゴリズムを使うと、ある 2者の間で、 一見ランダムな番号を交換するだけで、他のだれにも知られずに2人とも 共通の番号 (鍵) を手に入れることができます。手品のようなこのアルゴリズムは、 離散対数 (discrete logarithm) 問題という数学的なトリックを使っています。

まず最初にホスト A, B の間で、共通の素数 n と、べき乗の底 g を決めておきます (この 2つの数字は、第三者に知られてもかまいません)。 そして双方がランダムな数値 (それぞれ a, b とします) を生成し、 それぞれ ga mod n と gb mod n を計算して 相手に送ります。この後、ホスト A は a と gb mod n の値、 ホスト B は b と ga mod n の値を知っているので、 ここから共通の値 ga・b mod n が計算できます。 これが A, B 間の共通の暗号鍵になります。

なぜこれが安全なのでしょうか? たとえ第三者が ga mod n の値と gb mod n の値両方を盗聴できたとしても、n の値が十分に大きい場合は ここから元の a, b の値を導きだすことが非常に困難なためです。 これが離散対数問題です。たとえ ga mod n と gb mod n を掛けてみても、できあがる数値は ga+b mod n であり、これは ga・b mod n とは異なっています。 しかしそれぞれのホスト A, B 自身は自分の生成した値を知っているので、 ga・b mod n の値を正しく計算できるわけです。

DH鍵交換の優れているところは、各ホストの生成した a, b の値が 通信ごとに 1度しか使われず、暗号鍵が得られたらすぐに破棄できることです。 このため、もし第三者かが時間をかけて a, b の値を発見できたとしても、 次回の通信時にはすでに a, b の値は別のものに変わってしまっています。 この性質は forward security (前進的セキュリティ) と呼ばれています。 ただし、DH鍵交換は第三者が間に入ると暗号鍵をすりかえられる危険性がありますので、 実際に信頼できる暗号化通信をおこなうためには、鍵交換をしたあとで 相手が正しい通信相手であるかどうかを確かめるホスト認証が必要です。


Yusuke Shinyama