🔑

SSHのユーザー認証についてすこしだけ詳しくなるはなし

2022/11/11に公開

これは何の記事?

SSHの公開鍵を用いたユーザー認証の概要や、その仕組みについての入門的な記事です。

想定読者

  • SSHがよくわからない人
  • SSHを使うが、「公開鍵で認証してるんだよね」の中身がよくわかっていない人

私自身が曖昧な理解だったところを整理して書いたので、同じような方の参考になれば。

用語・概念の整理

SSHとは?

まず、そもそもSSHとはなにか、整理してみます。

  • SSH(Secure Shell)とはリモートホストのシェルを利用(今回の目的はこれ)したり、遠隔操作をするためのプロトコル(取り決め)
  • 上記のプロトコルを実装したソフトウェアを指す場合もある
  • SSHのプロトコルにはバージョン1, 2が存在する[1]
  • パスワード認証、公開鍵認証など複数の形式でユーザー認証を行うことができる
  • OpenSSHPuTTY(Windows)などさまざまな実装がある

公開鍵という言葉が出てきました。これも整理しておきます。

公開鍵認証とは?

公開鍵認証は次の仕組みです。

  • 鍵のペアを作成し、公開鍵をサーバーに共有し、秘密鍵を用いてクライアントからのデータへ署名して接続する形式

これだけだとよく分からないですね。あとでゆっくり見てみましょう。

OpenSSHとは?

  • SSHプロトコルを実装した最もよく使われている[2]ソフトウェア
  • 多くのMac/Linuxのsshコマンドは初期値ではこれを用いている。

ユーザー認証について

それでは本題に入って、ユーザー認証の目的から考えてみます。

「私はこのサーバーに登録されているユーザーAなのでログインさせてください」というリクエストがあったとき、正しいリクエストであればなにも問題が無いのですが

もしも悪意あるユーザーが「ユーザーA」さんを名乗った場合に、リクエストが通ってしまう問題が発生します。

なので、本当にリクエストを送っているのがユーザーAなのかどうかを調べる必要があります。

これがユーザー認証の目的で、よく使われている形式はIDとパスワードを用いたものです。
IDとパスワードが事前に登録したものと一致しているかどうかで、ユーザーからのリクエストを検証します。

RSA暗号方式を用いた電子署名でのユーザー認証

今回はパスワードではなく、電子署名を用いたユーザー認証について見ていきます。
SSHにおいて、公開鍵を用いたユーザー認証の大きな流れは次のようになっています。[3]

まず、ユーザーの公開鍵と秘密鍵のペアを生成し、公開鍵をサーバーへ登録します。

「私はこのサーバーに登録されているユーザーAです」というメッセージを秘密鍵を用いて変換したものを署名データとして扱います。

署名データは公開鍵を用いて「私はこのサーバーに登録されているユーザーAです」という元のメッセージに戻すことができます。

サーバーが事前に登録された公開鍵を用いて正しく戻せれば元の署名は正当だったとなりますし、別のメッセージになってしまえば、署名が間違っていたと判定できます。

公開鍵でのユーザー認証

もう一度SSHでのユーザー認証フローを見てみましょう。

ここで上記の④での署名はどのようなデータが入っているのでしょうか。
実際には形式のデータから作成しています。[4]

// 署名の中身
string    session identifier
byte      SSH_MSG_USERAUTH_REQUEST
string    user name
string    service name
string    "publickey"
boolean   TRUE
string    public key algorithm name
string    public key to be used for authentication

色々書かれていますが、一端流しましょう。
⑤の署名を送るパケットは以下の形式のデータです。
ユーザー名等と同時に、上記のデータから生成した署名データが含まれています。

// 署名を送るパケット
byte      SSH_MSG_USERAUTH_REQUEST
string    user name
string    service name
string    "publickey"
boolean   TRUE
string    public key algorithm name
string    public key to be used for authentication
string    signature // 上記のデータを用いて生成した署名

どちらにもpublic key algorithm nameという文言が出てきました。
これはどのような種類の公開鍵アルゴリズムを使用しているかを明示します。

SSHの署名に使えるアルゴリズム

SSHでは、署名アルゴリズム(public key algorithm name)に以下の形式を指定できます。

  • ssh-rsa
  • ssh-dss
  • pgp-sign-rsa
  • pgp-sign-dss
  • rsa-sha2-256
  • rsa-sha2-512
  • ssh-ed25519
  • ecdsa-sha2-*(*は識別子)

知らない単語に目がくらみますが、よく見るとrsa, shaという単語が何度か出ていますね。
これについて見ていきます。

RSAとは?

RSAとは公開鍵暗号のアルゴリズムのひとつです。
素因数分解の難解さをベースに作られています。

  • RSA暗号: 素数p, qがあり、n = pqのとき暗号化にn、復号にp・qを用いて、nを公開鍵とする
  • 秘密鍵はp,qから作る[5]
  • 署名に用いる場合は、秘密鍵を用いて送信データのハッシュ(元データから生成した短い文字列)を署名データに変換する
  • 送信データからハッシュをどのように生成するかのアルゴリズムは別途指定することができる

RSA署名の流れ

SHAとは?

SHAはハッシュを生成するアルゴリズムです。

  • SHA: Secure Hash Algorithm
  • 固定長のハッシュを作成するためのアルゴリズム
  • 現在はSHA-0, 1, 2, 3までバージョンがある
  • SHA-1: 現在は脆弱性により非推奨とされている
  • SHA-2[6]: SHA1の改良版、SHA-256, SHA-512はそのバリエーション(256, 512はハッシュの長さ)

公開鍵認証で使用している署名形式

たとえば、SSHで公開鍵署名アルゴリズムにrsa-shaを指定するとRSASSA-PKCS1-v1_5[7]という規格で定められたフォーマットの署名アルゴリズム(RSAを用いて変換しているもの)を用いて、SHA-1をハッシュ関数として用います。
同様にrsa-sha2-256を指定するとRSASSA-PKCS1-v1_5という規格で定められたフォーマットの署名アルゴリズムを用いて、SHA-2をハッシュ関数として256bitのハッシュを用います。

SSHでの指定 署名アルゴリズム ハッシュ関数
rsa-sha RSA SHA-1
rsa-sha2-256 RSA SHA-256

では、どの署名方式を使うかはどうやって決定しているのでしょうか?
答えは鍵の種類と、クライアント、サーバーのソフトのバージョンの組み合わせです。

鍵の生成とその種類

鍵を生成するには多くの場合ssh-keygenコマンドを用います。
ユーザーは鍵を生成する際に

$ ssh-keygen -t

-t(type)オプションで鍵の種類をdsa, ecdsa, ecdsa-sk, ed25519, ed25519-sk, rsaから指定することができます。
OpenSSH_9.0p1の場合では、オプションを指定しない場合rsaが指定されます。よってRSA暗号方式を用いた鍵のペアが生成されます。

ここで大事なのはrsaの鍵は署名時にrsa-sha2-512としてもrsa-sha(sha-1)としても使用できるということです。
このふたつのアルゴリズムはどちらもRSA暗号方式を用いており、ふたつの違いはハッシュ関数にSHA-2SHA-1のどちらを用いているかなので、鍵は互換があるということですね。

署名アルゴリズムとOpenSSHのバージョン

SSHでログインする際に、

$ ssh -i {鍵ファイル}

で鍵を指定します。
鍵ファイルとして、rsa鍵を指定したときに、クライアントのOpenSSHのバージョンが7.2以上のとき、署名にrsa-sha2-512(SHA-2)を暗黙的に使用します。
このとき、サーバー側が7.2未満などSHA-2に対応していない場合はrsa-sha(SHA-1)を利用します。

ここで注意すべきなのが、SHA-1はすでに非推奨であるという点です。
そのため、OpenSSHのバージョン8.8以降ではSHA-1の利用がデフォルトで禁止[8]されているということです。
つまり、クライアント側のOpenSSHのバージョンが8.8以上かつ、サーバー側がSHA-2に非対応のとき、SHA-1に切り替えることができずエラーとなります。

エラー例:send_pubkey_test: no mutual signature algorithm
→ このエラーは署名のアルゴリズムが双方で利用可能ではないことを示しています。

エラーの対処方法

上記のエラーの回避方法は3つあります。

  1. サーバー側でSHA-2を利用ができるようにソフトウェアをバージョンアップする
  2. クライアント側でrsa-shaの利用を明示的に許可する
  3. rsa以外の方式(Ed25519)の鍵を用いる

1. サーバー側でSHA-2を利用可能ができるようにソフトウェアをバージョンアップする

1は問題の原因がSHA-2の形式を利用不可能であることなので、利用可能なバージョンに変更するという方法です。
SHA-1までしか利用できない最新バージョンであるOpenSSH 7.1p2のリリースは2016-01-14なので、ここでアップデートが止まっているサーバーは他にもセキュリティ課題があるかと思います。
ですが、それだけの塩漬けサーバーを更新できるかというと…。

2. rsa-shaの利用を明示的に許可する

その場合の選択肢が2です。SHA-1の利用はデフォルトで禁止されているだけなので、クライアント側のconfigファイルにて明示的に許可することができます。

# .ssh/config
Host foo
HostName xxx.xxx.xxx.xxx
UserName foo
IdentityFile bar_rsa
PubkeyAcceptedAlgorithms +ssh-rsa  # 明示的に許可する

3. rsa以外の方式(Ed25519)の鍵を用いる

別の案が3です。双方のOpenSSHのバージョンが6.5以上であればEd25519という別のアルゴリズムの鍵を使用することができます。[10]

Ed25519とは?

Ed25519とは何でしょうか?実は少し上で出てきています
軽く説明すると次の通りです。

  • 比較的新しい署名アルゴリズム[11]
  • RSAとは異なる暗号方式であるEdDSAのうちひとつ
  • ハッシュ関数としてSHA-512(SHA-2)[12]を用いている

この鍵ではSHA-1を使わないため、上記の問題を回避できるということです。
鍵を生成するには

$ ssh-keygen -t ed25519

のコマンドを用います。
この鍵を用いてSSHアクセスを行います。

$ ssh -i {ed25519の鍵}

まとめ

  • SSHでは公開鍵の署名を用いたユーザー認証ができる
  • RSA署名は秘密鍵を使ってユーザー情報を署名にでき、公開鍵を使って復元できる仕組みを用いている
  • RSA署名はハッシュアルゴリズムを選択できる
  • ハッシュアルゴリズムSHA-1は2022年現在すでに非推奨である
  • どの署名アルゴリズムを用いるかは使用する鍵の種類と、クライアント・サーバー側のソフトウェアの対応状況によって決定される
  • Ed25519SHA-2を用いている

おまけ: SSHのバージョン確認方法

SSH接続にはクライアント、サーバー側のバージョンが重要であることがわかったので、それぞれの確認方法を記します。

クライアント側のバージョンを確認するにはssh -Vコマンドを使用します。

ssh -V # バージョンを表示
OpenSSH_9.0p1, LibreSSL 3.3.6 # OpenSSH_9.0p1が利用可能だとわかる

サーバー側のバージョンはshh接続時に-vを入れるとデバッグログとして表示されます

ssh {中略} -v
{中略}
debug1: Remote protocol version 2.0, remote software version OpenSSH_6.6.1

参考文献

資料は脚注に記しましたが、その他にも以下のサイトを参考にいたしました。

SSH: The Secure Shell (The Definitive Guide)
SSHの公開鍵ってなに?
OpenSSH 完全に理解した(笑)
「電子署名=『秘密鍵で暗号化』」という良くある誤解の話
公開鍵暗号と電子署名の基礎知識

脚注
  1. 今(2022)の主流はバージョン2 ↩︎

  2. 要出典 ↩︎

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

  4. RFC4252 ↩︎

  5. 作り方の数式のTeXを書きたい ↩︎

  6. SHA-2 ↩︎

  7. RFC8017 ↩︎

  8. OpenSSH 8.8 ↩︎

  9. (何度目かの)さようなら SHA-1 ↩︎

  10. OpenSSH 6.5 ↩︎

  11. エドワーズ曲線デジタル署名アルゴリズム
    ↩︎

  12. rfc8032 ↩︎

NCDCエンジニアブログ

Discussion