SSHのユーザー認証についてすこしだけ詳しくなるはなし
これは何の記事?
SSHの公開鍵を用いたユーザー認証の概要や、その仕組みについての入門的な記事です。
想定読者
- SSHがよくわからない人
- SSHを使うが、「公開鍵で認証してるんだよね」の中身がよくわかっていない人
私自身が曖昧な理解だったところを整理して書いたので、同じような方の参考になれば。
用語・概念の整理
SSHとは?
まず、そもそもSSHとはなにか、整理してみます。
- SSH(Secure Shell)とはリモートホストのシェルを利用(今回の目的はこれ)したり、遠隔操作をするためのプロトコル(取り決め)
- 上記のプロトコルを実装したソフトウェアを指す場合もある
- SSHのプロトコルにはバージョン1, 2が存在する[1]
- パスワード認証、公開鍵認証など複数の形式でユーザー認証を行うことができる
-
OpenSSH
、PuTTY
(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-2
とSHA-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つあります。
- サーバー側で
SHA-2
を利用ができるようにソフトウェアをバージョンアップする - クライアント側でrsa-shaの利用を明示的に許可する
- rsa以外の方式(
Ed25519
)の鍵を用いる
SHA-2
を利用可能ができるようにソフトウェアをバージョンアップする
1. サーバー側で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 # 明示的に許可する
Ed25519
)の鍵を用いる
3. rsa以外の方式(別の案が3です。双方のOpenSSHのバージョンが6.5
以上であればEd25519
という別のアルゴリズムの鍵を使用することができます。[10]
Ed25519とは?
Ed25519
とは何でしょうか?実は少し上で出てきています
軽く説明すると次の通りです。
この鍵ではSHA-1
を使わないため、上記の問題を回避できるということです。
鍵を生成するには
$ ssh-keygen -t ed25519
のコマンドを用います。
この鍵を用いてSSHアクセスを行います。
$ ssh -i {ed25519の鍵}
まとめ
-
SSH
では公開鍵の署名を用いたユーザー認証ができる -
RSA署名
は秘密鍵を使ってユーザー情報を署名にでき、公開鍵を使って復元できる仕組みを用いている -
RSA署名
はハッシュアルゴリズムを選択できる - ハッシュアルゴリズム
SHA-1
は2022年現在すでに非推奨である - どの署名アルゴリズムを用いるかは使用する鍵の種類と、クライアント・サーバー側のソフトウェアの対応状況によって決定される
-
Ed25519
はSHA-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 完全に理解した(笑)
「電子署名=『秘密鍵で暗号化』」という良くある誤解の話
公開鍵暗号と電子署名の基礎知識
NCDC株式会社( ncdc.co.jp/ )のエンジニアチームです。 募集中のエンジニアのポジションや、採用している技術スタックの紹介などはこちら( github.com/ncdcdev/recruitment )をご覧ください! ※エンジニア以外も記事を投稿することがあります
Discussion