Photo by charlesdeluvio on Unsplash

SSH 到底在幹嘛?幫你看懂 SSH 連線與驗證流程

本文為技術入門普及文,為方便閱讀,以下名詞將在首次出現時同時標示中英文,之後統一以中文描述。

讀者如果有不熟悉的名詞可以透過下列此表進行比對、確認說明。

類別名詞中文名稱說明
協定與標準SSH(Secure Shell)安全殼層協定一種提供安全遠端連線的網路協定。
OpenSSH開源 SSH 實作Linux 與 Windows 系統常見的 SSH 軟體。
RFC 4253 / RFC 4252 / RFC 4254SSH 標準規範定義 SSH 傳輸層、認證層與連線層協定。
IETF(Internet Engineering Task Force)網際網路工程任務組制定並維護網路標準的組織。
連線角色Client用戶端發起 SSH 連線的主機。
Server伺服器端接收並處理 SSH 請求的主機。
SSH 登入五階段版本號協商(Version Exchange)確認雙方支援的 SSH 版本。
演算法與金鑰交換(Algorithm Negotiation + Key Exchange)協商加密與驗證演算法,生成會話金鑰。
使用者身分驗證(User Authentication)驗證使用者合法性(密碼或金鑰)。
會話請求(Channel Request)建立會話通道,準備執行命令或開啟 shell。
資料交換(Data Exchange)雙向傳輸與加密解密資料。
加密與演算法Diffie-Hellman 金鑰交換一種主流的建立共享密鑰的演算法。
金鑰交換演算法(Key Exchange Algorithm)用於產生臨時會話金鑰。
加密演算法(Encryption Algorithm)對傳輸資料加密。
封包驗證演算法(MAC, Message Authentication Code)確保封包內容未被竄改。
壓縮演算法(Compression Algorithm)減少資料傳輸量。
會話與金鑰會話金鑰(Session Key)雙方共用,用於加密通訊內容。
會話識別碼(Session ID)標識此次 SSH 連線,防止重放攻擊。
共享密鑰(Shared Secret)Diffie-Hellman 計算所得的共同密鑰。
驗證與金鑰管理密碼驗證(Password Authentication)使用帳號密碼登入。
公鑰驗證(Public Key Authentication)以非對稱加密進行登入驗證。
ssh-keygen生成公鑰與私鑰的工具。
公鑰(Public Key)用於驗證簽章。
私鑰(Private Key)用於簽章,不可外傳。
authorized_keys伺服器端紀錄授權登入公鑰的檔案。
~/.ssh/儲存金鑰與設定檔的目錄。
封包與控制訊息CHANNEL_OPEN_CONFIRMATION伺服器回覆通道開啟成功。
CHANNEL_OPEN_FAILURE伺服器回覆通道開啟失敗。

本文主要參考 https://www.cdxy.me/?p=394

網路上有很多關於 SSH (安全殼層協定)的說明文章,但我只找到這篇不會有各種專有名詞滿天飛,敘述簡單清晰,是比較好懂的一篇文章,在 SSH 的了解上對我幫助極大,推薦給大家。

雖然說是這樣說,但還是有很多專有名詞,為了降低閱讀難度,我在上面整理了一張表,請讀者眼花撩亂了就看一眼這張表吧,多少會有幫助。

直接進入主題。

問題與五階段

  • SSH 從發出請求到連線、能夠傳輸,中間到底經過了什麼?
  • 我準備好了金鑰(Key Pair),也能順利連線,但 SSH 是怎麼驗證的?
  • 做為一個以安全性為主要賣點的連線方式,它到底如何保障傳輸的安全性?

這篇文章要來嘗試解答這些問題,主要說明 SSH 整個連線會經過的過程

首先,SSH 從發出請求到連線、能夠傳輸,可分為五個階段:

  1. Version Exchange 版本號協商
  2. Algorithm Negotiation + Key Exchange 密鑰與演算法協商
  3. User Authentication 使用者身分驗證
  4. Channel Request 會話請求
  5. Data Exchange 會話交換

💡 補充:

上面五個階段依據 SSH 協定系列(RFC 4253、RFC 4252、RFC 4254)而來。

SSH 協定系列 是由 IETF(Internet Engineering Task Force) 定義。

OpenSSH ⇒ 目前主流的 SSH 連線軟體,在 Linux 和 Windows 系統上廣泛使用。

一、版本號協商(Version Exchange)

進入到第一個階段,以順序排序,雙方會有以下動作:

  1. 用戶端 Client 向 伺服器端 Server 發起 SSH 連線。 → 對伺服器端的 port 22 發送連線請求。
  2. 伺服器端傳送自己的版本號給用戶端。 例如:SSH-2.0-OpenSSH_9.6
  3. 用戶端傳回自己的版本號。 例如:SSH-2.0-OpenSSH_9.4
  4. 雙方比對版本是否相容。 若能相容 → 進入下一階段; 若不相容 → 結束連線。

💡 補充:

  • 一般來說,SSH 連線預設會使用 port 22,但不是必須得用 port 22。
  • 理論上不會出現「用戶端說相容、伺服器端說不相容」的狀況,若真的不同(例如伺服器端只支援 SSH-1.5),用戶端一收到就直接斷線

二、密鑰與演算法協商(Algorithm Negotiation + Key Exchange

當連線建立後,用戶端與伺服器端會交換各自支援的算法清單,並從中挑出雙方都能理解的一組組合

在這個階段,最重要的是雙方會生成:

  • Session Key( 會話金鑰) → 用戶端與伺服器端各自透過 Diffie-Hellman 算法,共同算出一把只有他們知道的共享秘鑰(shared secret)。
    (不一定是 Diffie–Hellman,只是它比較常見)
    這把會話金鑰後續會被用來:
    • 加密傳輸資料
    • 驗證封包完整性(MAC)

注意:這個金鑰是「暫時」的,每次重新連線都會重新算一次。

  • Session ID(會話 ID) →標識整個 SSH session (SSH 會話)(就像一張身分證)。

💡 補充:

  • 每次協商(Negotiation)雙方都會確定出三大類算法
類別功能範例用途階段
① 金鑰交換算法 (Key Exchange Algorithm)決定雙方如何一起產生臨時的「共同密鑰 (session key)」diffie-hellman-group14-sha256curve25519-sha256建立安全通道時使用
② 加密算法 (Encryption / Cipher)負責如何加密後續傳輸的資料(使用上一步產生的 session key)aes128-ctraes256-ctrchacha20-poly1305對封包內容加密
③ 封包驗證算法 (MAC / Message Authentication Code)驗證封包內容是否被竄改,確保完整性hmac-sha2-256hmac-sha1對每個封包附加驗證碼
有些實作還會附帶 ④ 壓縮算法 (Compression Algorithm)
  • 「shared secret」不是 session key,其只是生成 session key 的中間值,可以這樣理解:
    shared secret = 後續「衍生出 session key」的基礎材料

三、使用者身分驗證 User Authentication

結束上個階段,此時已有會話密鑰與會話 ID,後續所有資料皆使用該密鑰加密傳輸。

SSH 允許多種驗證方式,主要是:

  • 帳號及密碼驗證
  • 公鑰私鑰驗證

帳號密碼驗證

流程如下:
  1. 用戶端準備登入資訊: 包含使用者名稱、欲採用的驗證方式、密碼等。
  2. 用戶端使用前一階段產生的 會話密鑰 將這些資料加密後發送給伺服器端
  3. 伺服器端收到後,用相同的 會話密鑰 解密封包,取得帳號與密碼。
  4. 伺服器端檢查帳號是否存在,密碼是否正確(例如查 /etc/shadow 或 PAM 模組)。
  5. 若驗證通過 → 登入成功;若錯誤 → 拒絕連線。

公鑰私鑰驗證

這裡我自己會分為兩個流程:準備、實際驗證。

準備流程:
  1. 使用指令 ssh-keygen 生成一對金鑰檔案:
    • 公鑰:id_dsa.pub
    • 私鑰:id_dsa
    這裡的公鑰、私鑰名稱為示意,你的公私鑰不一定是這個名字。
  2. 將公鑰放置於伺服器上對應帳號的 .ssh 目錄下的 authorized_keys
  3. 將私鑰放置於 用戶端,用於證明身分,不可外傳
實際驗證流程:
  1. 用戶端發送要操作的伺服器端使用者及公鑰資訊(以 Session key 加密) 用戶端對伺服器端說:「我要以某個使用者身分登入,並使用這把公鑰驗證。」
  2. 伺服器去 authorized_keys 查找該使用者的公鑰
  3. 若存在 → 發 challenge 給你(下一階段)。 找不到 → 直接回「Permission denied (publickey)」。

💡 補充:

  • .ssh/ 是儲存 SSH 設定與金鑰的目錄 裡面可以包含多種檔案,例如:authorized_keys
  • authorized_keys 是一個純文字檔案,每一行代表一個允許登入該帳號的公鑰內容。
  • 使用公鑰私鑰驗證登入時,用戶端用私鑰簽名,伺服器端用公鑰驗證。
    動作 ⇒ 伺服器端提供要簽的內容、用戶端對資料簽章。
  • 如果 .ssh 或是 authorized_keys 權限太開放,同樣拒絕登入(不夠安全)

四、會話請求 Channel Request

當使用者身分驗證成功後,SSH 進入「建立會話通道(Channel)」的階段。

這個階段主要讓用戶端告訴伺服器端:

「我現在想開一個通道,用來執行某個動作(例如開 shell、執行指令、傳檔案等)。」

若請求成功 ⇒ 伺服器回覆 CHANNEL_OPEN_CONFIRMATION

若失敗 ⇒ 回覆 CHANNEL_OPEN_FAILURE

五、會話交換 Data Exchange

然後就可以進行雙向資料傳輸了,流程如下:

  1. 用戶端將要執行的命令(或輸入的操作)加密後傳給伺服器端。
每一個封包都會經由前面建立的 Session key 加密與簽章驗證(MAC)。
  1. 伺服器端收到後,用 Session key 解密並執行指令。
可能是開啟 shell、執行單一命令、或傳送檔案。
  1. 伺服器端將執行結果加密回傳給用戶端。
  2. 用戶端解密後顯示在終端機上。

耍!


留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *