SSHの公開鍵認証における良くある誤解の話

# はじめに
## 背景
これを知っていたからと言って何かの役に立つかというと、そうでない人の方が多いとは思うのですが、いい加減、SSHの公開鍵認証の説明がデマばかりなので、少しはちゃんとした説明もあった方が良いかもな、ということで記事にしました。

## SSHについて
皆さんご存知の通り、SSHはSecureSHellの略であり、暗号によって保護された通信を通じて遠隔操作を行う[^1]アプリケーション、またはプロトコル[^2]を指します。
Linux等のUNIX系OSの遠隔操作[^3]、またはネットワーク機器の管理に広く使われており、その実装としてはOpenSSHが有名です。

ところでSSHには、接続してきたユーザが誰なのかを識別する**認証機能**があり、その方式として**パスワード認証**と並んで最もメジャーなのが公開鍵暗号を用いる**公開鍵認証**です。[^4]

しかし…。残念なことに、公開鍵認証の設定や使い方はともかくとして、その仕組みに対する説明[^5]はかなり誤りが多い状況なのです。[^6]

# 公開鍵認証の仕組み
## 良くある説明
例として、IBM developerWorksの[OpenSSH キー （鍵） の管理: 第 1 回](https://www.ibm.com/developerworks/jp/linux/library/l-keyc/index.html)(2001年)を取り上げてみます。[^7]
※強調は@angel_p_57による

> ここから先がかなりおもしろいところです。Remoteboxの**sshdは乱数を生成**、先にコピーしておいた**公開鍵を使用してそれを暗号化**します。次いで、**この暗号化した乱数を**localboxで実行されている**sshに送り返します**。それを受けてsshは、**私有鍵を使用してこの乱数の暗号化を解除してそれをremoteboxに送り返します**。これは要するに「ほら、対応する私有鍵を間違いなく持っていますよ。メッセージの暗号化を解除できましたよ。」と言っていることになります。最終的にsshdは、このsshが対応する私有鍵を所有していることから、ログインを許可してよいと判断します。

これを図にすると次のようになると言えます。が、これは完全な誤りなのです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/74396/de116aaa-9a5f-365e-d908-c54e26405503.png)

巷に出回っている説明は、細かい違いはあれど、大体これに該当します。

…なぜこれが間違っているのか? それは**実態に合ってないから**という他はないのですが、実態を知らなくてももっと単純に判断できます。
それは、公開鍵認証に使うのは**電子署名の鍵**[^8]であって、そもそも**暗号化・復号という機能を想定していないもの**だということ。なので、このような仕組みになるはずがないのです。

## 妥当な説明
ということで、正しくは**電子署名をどう使うか**という説明になるはずです。
概要としては次の図のようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/74396/97146ad7-2d05-84c4-35ab-791825a11200.png)

単純に言うと、**ユーザが自身の秘密鍵を使って署名を生成**し、その署名を**サーバ側が公開鍵を使って検証**できれば、その署名は確かに正当なユーザが作成したものだからと判断し、認証成功ということです。もちろん、ユーザと公開鍵との対応が適切かどうかも照合を行います。

ただし注意が必要なのは、認証の前の鍵交換、中でもその手続きによって共有される**セッションID**が絡んでくることです。
鍵交換はDHやECDHといった鍵交換方式[^9]により行われ、第三者には推測困難な状態[^10]で秘密情報を共有します。セッションIDはその秘密情報から導出されますが、同時に当事者にすら内容のコントロールが困難な、つまり**狙った値を故意に作り出せない**ようにされています。

このセッションIDを署名対象データに混ぜておくことで、認証のために生成した**署名が流用されることを防ぐ**のです。
※巷に出回っている誤った説明は、実態と合ってないという点が一番の問題ですが、この流用への対策に相当する考慮が全くないという点も問題と言えます。

# 公開鍵認証の特長
## 本当に公開鍵認証の方が良い?
巷では「パスワード認証よりも公開鍵認証の方が安全で良い」と言われますが、果たして本当でしょうか?

結論から言えば、**公開鍵認証の方が良い**と言って差し支えないと思います。
が、結論が同じとは言え、そこに至る理由にも、一般で言われているものとは多少の相違がありますので、本記事で取り上げていきたいと思います。

## 公開鍵認証とパスワード認証の違い
先に公開鍵認証とパスワード認証の違い、そして公開鍵認証のメリットを説明します。

公開鍵認証において、サーバに登録するのはユーザの公開鍵です。そして、認証時には秘密鍵そのものではなく、生成した署名データ、それも流用困難なものをサーバに提示します。そのため**第三者はもちろんサーバであっても秘密鍵を入手したり、署名を悪用することは困難です**。
一方で、パスワード認証の場合、認証時にはパスワードそのものをサーバに提示します。この認証フェーズでは既に通信が暗号で保護されていますので、第三者にパスワードが盗聴されるわけではありませんが、**当のサーバに悪意があり[^11]かつ他所で同じパスワードを使用している場合**、パスワード漏洩・他所への悪用のリスクがあります。

つまり公開鍵認証最大のメリットは、パスワード認証に使うパスワードと違って**同一の鍵を各所に使い回すことにリスクがほぼない**ことです。[^12]

もちろん、パスワード認証の場合、うっかりパスワードを取り違えて**つい他所用のパスワードをサーバに漏らしてしまった**り、管理が面倒になって**つい同じパスワードを使い回してしまった**り、そもそも人間の頭で覚えて使おうとするあまり**つい強度の不十分なパスワードを使ってしまった**りという、極めて人間臭いリスクもないとは言えず、公開鍵認証には**フールプルーフ(意訳:「ついうっかり」対処)的なメリット**も現実には大きいのかもしれませんが、技術上は本質的なものではないことにご注意ください。

## 良くある理由付け
では今度は、巷で良く言われる理由付けを取り上げたいと思います。

例えば、レンタルサーバ等のサービスで有名なさくらインターネットの[ナレッジ](https://knowledge.sakura.ad.jp/3543/)には次のようにあります。

> 現時点(**※***パスワード認証が有効な状態*)では、(略)パスワードが分かれば誰でもログインできてしまいます。
(略)
これにより(**※***公開鍵認証のみ有効な状態にすることで*)、パスワード総当り攻撃などで第三者に不正にsshのログインを試みられても、鍵が盗まれない限り破られることはありません。

なお※は@angel_p_57による補足です。

まず「(パスワード認証が有効なら)パスワードが分かれば誰でもログインできる」これは確かにその通りですが、公開鍵認証であっても、当人と同じ秘密鍵を持てば誰でもログインできることには変わりません。秘密鍵と言えど、一般のパスワードと同じくテキストとして表現可能なデータですから、パスワードはどこでも使える一方で秘密鍵は特定のマシンでしか使えないとするのは偏りのある説明です。

また「パスワード総当り攻撃など」と脅威を挙げていますが、それで問題になるのは脆弱なパスワードを設定していることが前提です。
十分な強度を持たせたパスワードであれば総当たり攻撃が脅威になるレベルではありませんし[^13]、それを言うのであれば、DSAやECDSAといった方式では、鍵の中でも核となる小さな秘密情報さえあれば鍵を復元することは可能[^14]ですから、その秘密情報を総当たりして鍵を試すような攻撃も問題視するはめになるでしょう。

ということで、**なぜ公開鍵認証の方が安全かの説明になっていない**のです。

…ただ恐らくは、このような説明が出回るのには、**パスワードというものは人間の記憶できる範囲で設定する、ある程度推測可能な代物なのだ**という前提があるのでしょう。
実際にそのように扱われている面もあろうかとは思いますが、**ユーザのパスワードの取り扱いに対する意識の問題と認証方式自体の問題とは区別すべき**かと思います。[^15]

なお、たまに「パスワード認証は平文でパスワードが流れる」と説明しているところもありますが、前項で説明した通り、認証のフェーズでは既に通信自体が暗号で保護されていますから、「平文」というのは不適切です。

# 終わりに
## まとめ
というわけでまとめです。

* 公開鍵認証は、**署名を提示できるのが秘密鍵の所有者のみ**という電子署名の性質を利用した認証方式である。
* 良くある説明、「公開鍵認証では乱数を公開鍵で暗号化し…」は誤りである。
* 公開鍵認証の、パスワード認証と比較したメリットは、**同一の鍵を各所の認証に使い回すことのリスクがほぼないこと**である。
* 良くある説明での、「公開鍵認証の方が安全」の理由付けは、むしろユーザが適切なパスワード取り扱いをできないことを前提としており、技術的に妥当とは言い辛い。

もっと詳しく知りたい方や、話の裏を取りたい方には、脚注にもある[RFC4252の和訳](https://www.unixuser.org/~haruyama/RFC/ssh/rfc4252.txt)を一読されることをお勧めします。

[^1]: **SSHの機能**: 遠隔操作の中には、scpやsftpといったファイル転送機能もあります。更に遠隔操作を行うのみにとどまらず、通信を内部的に多重チャネル化して扱うことで、VPNめいた通信のトンネル機能も実現します。
[^2]: **SSHプロトコル**: SSHプロトコルには、バージョン1(1.5),2の2種類が存在しますが、1は遠い昔に非推奨となっており、まず全く使われていないはずなので、全て2の話だと思ってください。というか、1の方は正式なRFCにすらなっていません。
[^3]: **遠隔操作**: 近年だと、githubをはじめとするオンラインのgitリポジトリを操作するという目的で、なじみ深い方も多いかもしれません。
[^4]: **認証の種類**: パスワード認証、公開鍵認証の他に、ホストベース認証、キーボードインタラクティブ認証、GSSAPI認証があります。最後の2つについてはSSHアプリケーションが独自に認証を行うというよりも、外部のライブラリ等と連携して認証を実現することを想定した方式です。キーボードインタラクティブ認証は「チャレンジレスポンス」の方が通りが良いかも知れません。ワンタイムパスワードで認証するために使われたりします。GSSAPIはKerberos(Windows Active Directory含む)との連携が主となるようです。
[^5]: **仕組みに対する説明**: sshアプリケーションを実際に作成する人でなければ、仕組みを誤って認識していたとしても実害は無いとも言えるのですが。ただ、デマの再拡大の元にはなるので、個人的には気持ち悪いところです。
[^6]: **ほとんど全て**: 各種技術情報サイトも壊滅的です。とは言えもちろん、sshアプリケーションのマニュアル(流石に)含め、適切な説明のものも存在します。例えば日本語でちょっと検索した感じだと次の通り。

 * [PuTTYマニュアル](https://www.ranvis.com/doc/putty/man/Chapter8.html#pubkey-intro)
 * [ssh.comのSSH Tectia Clientマニュアル](https://www.ssh.com/manuals/client-user/60J/userauth-pk.html)
 * atmarkITの[実践でも役立つLPICドリル](http://www.atmarkit.co.jp/ait/articles/0906/12/news114.html)
 * ブログ[Engineer of the lawyer](http://thcom.hatenablog.com/entry/2013/07/04/224618)
 * 書籍[ゼロからはじめるLinuxサーバー構築・運用ガイド 動かしながら学ぶWebサーバーの作り方](https://www.shoeisha.co.jp/book/detail/9784798146379)
 * 意外だったのが、[情報処理技術者試験H29秋過去問](https://www.fe-siken.com/kakomon/29_aki/pm01.html)
 * また、東京電機大学の[SSHの進化](http://www.net.c.dendai.ac.jp/~daniel/)、こちらは情報こそ古いものの、廃れたSSHv1との比較情報があり、今では逆に貴重と言えます。
 * 最後に、日本人として情報の翻訳等OpenSSHに関わってこられた春山氏による[RFC4252の和訳](https://www.unixuser.org/~haruyama/RFC/ssh/rfc4252.txt)(というかこれだけ読めば良いって話もあるんですが)


[^7]: **IBMの記事を取り上げた理由**: 2001年と古い記事ながらなぜ取り上げたかと言うと、根本的に今出回っている説明がここから変わっていない点と、既に当時稲村氏により[公開鍵暗号による Challenge & Responseって？](http://www.vicus-oryzae.com/gorua/c-and-r.html)という記事で誤りを指摘されていたという経緯があるからです。
[^8]: **電子署名の鍵**: 現在SSHで使用できる方式としては、RSA,DSA,ECDSA,EdDSA(ed25519)です。これら電子署名も、(広義の)公開鍵暗号の1種です。暗号化・復号の機能を持つのは狭義の公開鍵暗号であり別物です(RSAに限っては電子署名用の鍵を暗号用に流用もできるのですが)。「いや電子署名でも暗号化とかあるんじゃないの」という人は、拙記事[電子署名の基礎知識](/angel_p_57/items/437ca6235defc938b97d)や、[「電子署名=『秘密鍵で暗号化』」という良くある誤解の話](/angel_p_57/items/d7ffb9ec13b4dde3357d)をご参照ください。
[^9]: **鍵交換方式**: DHやECDHといった鍵交換方式も、広義の公開鍵暗号に分類される技術です。
[^10]: **第三者には推測困難**: とは言え、通信相手が識別できていなければ「第三者」もへったくれもないので、鍵交換と同時にホスト認証が行われ、サーバが何者であるかをユーザが識別します。もし識別をさぼった場合には、なりすまし(中間者攻撃含む)による通信の乗っ取りを許してしまう恐れがあります。
[^11]: **サーバの悪意**: 「SSHサービス提供側を疑えってのか!」と思われるかも知れませんが、サーバ側が例えば乗っ取られた場合のことも含めたリスクの話をしています。
[^12]: **同一の鍵を各所に**: なので当然、自分の公開鍵を文字通り公開することにもリスクは特にありません。どこかで「公開鍵と言えどサーバに登録する以外で公開すべきではない」という意見を見た記憶があるのですが、的外れと言えましょう。
[^13]: **パスワードの強度**: 最近のLinuxの場合、512bitのハッシュ化パスワードをサポートしますから、十分に長いパスワードを設定すれば256bitの強度を見込むことができます。一方で、OpenSSHでのECDSAのデフォルトは256bit鍵で、これは128bitの強度に相当します(参考:[鍵長に対するNISTのレポート](https://www.keylength.com/en/4/))。だから単純にパスワードの方が良いということはないのですが、総当たりに対する強度は十分に有しうるということです。
[^14]: **鍵の復元**: 拙記事[ECDSA鍵の復元テスト](/angel_p_57/items/4e5ffe6aac41a179c577)でECDSAの鍵を復元する話を挙げています。なお、だからといってECDSAという方式に問題があると主張しているわけではないので念のため。鍵データのファイルサイズは数百バイトオーダーになりますが、そのコアとなるのはせいぜい数十バイト程度ですよということです。
[^15]: **区別すべき**: 実際のところはそのような話は百も承知で、ただ、「一般のユーザが適切にパスワードを扱えるわけねぇだろ!! だから禁止にしてしまえ!」とストレートに言えないがために、敢えて婉曲表現をしている可能性は否定できません。