1. はじめに
この記事では、エンジニアなら毎日使っていると言っても過言ではない「SSH」が、なぜ安全なのか、その通信の裏側をステップごとに分かりやすく解説していこうと思います。
2. SSH接続の全体像
まずは、SSH接続が確立されるまでの全体の流れを見てみましょう。クライアントがサーバーに接続しようとしてから、安全な通信が始まるまで、裏側ではこんなやり取りが行われています。
この図のステップを、これから一つずつ深掘りしていきます。
3. Step 1-2:通信前のネゴシエーション
本格的な通信を始める前に、クライアントとサーバーは「どんな言葉(暗号方式)で話すか」というルールを決める必要があります。
-
バージョン交換: まずはSSHのバージョンをお互いに伝えます。「SSH-2.0-OpenSSH_8.2p1」のような文字列を交換しています。バージョンが合わないと、この時点で接続は切断されます。
-
アルゴリズム交換: 次に、使える暗号技術のリストをお互いに提示します。具体的には、以下の4種類のアルゴリズムについて、「私はこれとこれが使えるよ」とリストを交換し、両者が合意した方式を使います。
-
鍵交換アルゴリズム (例:
ecdh-sha2-nistp256
) -
サーバーホストキーアルゴリズム (例:
ecdsa-sha2-nistp256
) -
共通鍵暗号アルゴリズム (例:
aes256-gcm@openssh.com
) -
メッセージ認証コード(MAC)アルゴリズム (例:
hmac-sha2-256-etm@openssh.com
)
-
鍵交換アルゴリズム (例:
このフェーズで、意図的に古い(脆弱な)アルゴリズムを使わせようとする攻撃を「ダウングレード攻撃」と言います。最近のSSHクライアント/サーバーは、安全でないアルゴリズムをデフォルトで無効化するなどの対策が取られています。
4. Step 3-4:ホスト認証とセッション鍵の生成
暗号方式のルールが決まったら、いよいよ通信相手の確認と、暗号化の準備に入ります。
-
ホスト認証: 「接続先のサーバーが、本当に目的のサーバーか?」を確認するステップです。サーバーは自身の「ホストキー(秘密鍵)」で署名したデータをクライアントに送ります。クライアントは、あらかじめ知っている(または初めての接続時に保存した)サーバーの「ホストキー(公開鍵)」を使ってその署名を検証します。
- この仕組みにより、中間者攻撃(Man-in-the-Middle Attack) を防いでいます。
- クライアント側では、検証したサーバーのホストキーを
~/.ssh/known_hosts
ファイルに保存しています。初めて SSH 接続する際によく見る「Are you sure you want to continue connecting (yes/no)?」は、このファイルにキーがまだ無い時に聞かれています。
-
鍵交換 (DH/ECDH): ホストの安全性が確認できたら、この通信でだけ使う「使い捨ての鍵(セッション鍵)」を生成します。ここであの「公開鍵暗号」の出番です。
- ディフィー・ヘルマン(DH)鍵交換 により、たとえ通信経路が盗聴されていたとしても、盗聴者にはバレずに、クライアントとサーバーだけが同じ「セッション鍵」を共有できます。
- このセッション鍵こそが、この後の通信をすべて暗号化する「共通鍵」になります。
共通鍵の交換後、直ちに暗号化セッションが確立され、以後の通信が暗号化されるようになります。
5. Step 5:ユーザー認証
最後に、サーバーにログインするユーザーが「許可された本人か?」を確認します。
-
公開鍵認証: パスワードの代わりに、ユーザーが持つ「秘密鍵」で署名したデータをサーバーに送ります。サーバーは、あらかじめ登録されているユーザーの「公開鍵」で署名を検証します。
- パスワードのように固定の情報をネットワークに流さないため、非常に安全です。総当たり攻撃のリスクもより低くなります。
6. Step 6-7:暗号化通信と改ざん防止
全ての準備が整い、ここからようやく、ls
や cd
などのコマンドが安全にやり取りされます。
-
暗号化通信 (AES/ChaCha20): Step 4で生成した「セッション鍵」を使って、通信内容をすべて暗号化します。これで、第三者にデータを盗み見られることはありません。
-
MAC (HMAC): 送られてきたデータが、途中で誰かに書き換えられていないか(完全性)をチェックするための仕組みです。セッション鍵と通信内容から計算したハッシュ値(MAC値)をデータに付与します。受信側は同じようにMAC値を計算し、送られてきたMAC値と一致するかを検証します。一致しなければ、データが改ざんされたと判断して破棄します。
7. まとめ
SSHがなぜ安全なのか、その裏側のステップをご理解いただけたでしょうか?
理論を理解した上で、最後は「設定」でそのセキュリティを盤石なものにしましょう。Linuxに存在するSSHの主要な設定ファイルを紹介します。
サーバーサイドの要塞化: /etc/ssh/sshd_config
サーバーを管理するなら、このファイルの設定は必須です。外部からの接続ルールを定義します。
-
パスワード認証の無効化とrootログインの禁止
# パスワードでのログインを不許可に(公開鍵認証のみ許可) PasswordAuthentication no # rootユーザーでの直接ログインを不許可に PermitRootLogin no
-
強力な鍵を使う: RSAなら2048bit以上、またはよりモダンなEd25519を使いましょう。
# Ed25519で鍵を生成 ssh-keygen -t ed25519
-
弱い暗号方式を無効化する: 古い暗号方式は脆弱性の温床です。推奨される強力なものだけを許可しましょう。
# /etc/ssh/sshd_config の設定例 KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256 Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
クライアントサイドの設定: ~/.ssh/config
クライアントサイドでは~/.ssh/config
によってユーザー個人の接続設定を定義できます。(システム全体の設定は /etc/ssh/ssh_config
)
ssh -i ~/.ssh/my_key.pem user_name@192.168.1.1 -p 2222
のような長いコマンドが、ssh myserver
だけで済むようになります。
-
~/.ssh/config
の設定例この設定をしておけば、# 作成コマンド: touch ~/.ssh/config && chmod 600 ~/.ssh/config # Webサーバーへの接続設定 Host web-server HostName 192.168.1.10 User webadmin Port 22 IdentityFile ~/.ssh/web_server_key # DBサーバーへの接続設定 Host db-server HostName db.example.com User dbuser Port 2222 IdentityFile ~/.ssh/db_server_key # 10秒ごとに生存確認パケットを送信 ServerAliveInterval 10 # GitHubへの接続設定 Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519_github
ssh web-server
やssh db-server
だけで、指定したユーザー、鍵、ポートで接続できるようになります。
SSHの仕組みを正しく理解し、サーバーとクライアント両サイドの設定を最適化することで、日々の開発をより安全で快適に進めていきましょう