SSH 協議基本原理及 wireshark 抓包分析

擁抱心中的夢想發表於2018-09-27

一、SSH協議簡介

我們經常會使用ssh username@hostIp命令登陸我們的linux伺服器,如下圖所示:

我們也明白這是使用了SSH協議進行登陸,但我們想知道的是,為什麼可以使用SSH協議進行登陸,而且為什麼使用SSH就是安全的,其背後的原理是什麼?下面我們就一起來探討下這幾個話題。

當然啦!如果現在你手頭上有相關公網可訪問的雲主機,那麼請你登陸你的雲主機,然後執行grep sshd.*Failed /var/log/secure命令看看,或許你會驚訝的發現有很多輸出日誌,你就會明白到底有多少人想嘗試登陸你的主機了。

簡單地說,SSH協議是建立在不安全的網路之上的進行遠端安全登陸的協議。它是一個協議族,其中有三個子協議,分別是:

  • 1、傳輸層協議[SSH-TRANS]:提供伺服器驗證、完整性和保密性功能,建立在傳統的TCP/IP協議之上。
  • 2、驗證協議[SSH-USERAUTH]:向伺服器驗證客戶端使用者,有基於使用者名稱密碼和公鑰兩種驗證方式,建立在傳輸層協議[SSH-TRANS]之上。
  • 3、連線協議[SSH-CONNECT]:將加密隧道複用為若干邏輯通道。它建立在驗證協議之上。

這裡不對SSH協議做更加詳細的介紹,我們可以自行百度一下。下面的寫作思路將先通過wireshire抓包分析SSH協議上述三個流程,最後將提出整個學習過程中小編遇到的問題進行探討。

二、SSH協議握手過程分析

在接著下面的內容之前,這裡有幾個概念非常重要!

  • 1、會話金鑰 key:key是通過客戶端和伺服器之間通過諸如D-H演算法協商出來的。
  • 2、公鑰 pub key:pub key成為伺服器主機金鑰server_host_key,用於SSH-TRANS傳輸協議進行伺服器驗證,說白了就是客戶端去驗證伺服器用的

SSH協議握手過程大致流程如下圖所示:

下面是小編通過wireshire工具抓的資料包,讓我們分別一步步進行分析:

  • 1、TCP三次握手建立連線

我們都知道,TCP協議有個叫三次握手的過程,從上面圖中可以看出序號(647-649)即是TCP連線建立過程。

NO. 描述 seq Win ACK 解釋
647 第一次握手 0 8192 seq = 0表示客戶端當前的TCP包序列號
648 第二次握手 0 14600 1 seq = 0,表示伺服器端當前的TCP包序列號
ack = 1(客戶端seq + 1),表示對客戶端第 seq = 0 的TCP包進行應答
649 第三次握手 1 65536 1 seq = 1,表示客戶端端當前的TCP包序列號
ack = 1(伺服器seq + 1),表示對伺服器端第 seq = 0 的TCP包進行應答
  • 2、SSH版本協議交換

上圖序號(647-649)即是SSH版本協議交換過程。

NO. 描述 解釋
650 協議版本協商 伺服器將自己的SSH協議版本傳送到客戶端,格式為:SSH-protoversion(版本號)-softwareversion(自定義) SP(空格一個,可選) comments(註釋,可選) CR(回車) LF(換行)
651 協議版本協商 客戶端將自己的SSH協議版本傳送到伺服器,格式為:SSH-protoversion(版本號)-softwareversion(自定義) SP(空格一個,可選) comments(註釋,可選) CR(回車符) LF(換行符)

這一步其實沒什麼高大上的內容,就是傳送一個格式為SSH-protoversion-softwareversion SP comments CR LF的位元組流而已。

  • 3、金鑰協商key

上圖序號(652-677)即是SSH版本協議交換過程。

金鑰協商過程從客戶端和伺服器相互發出Key Exchange Init請求開始,主要是告訴對方自己支援的相關加密演算法列表、MAC演算法列表等。

最後協商成功之後,將會生成一個對稱加密會話金鑰key以及一個會話ID,在這裡要特別強調,這個是對稱加密金鑰key,不要和公鑰相混淆了,公鑰和金鑰在上面開頭已經著重強調兩者的區別了,公鑰是給客戶端去驗證伺服器用的。

在這一步中,公鑰會從伺服器傳送到客戶端:

而會話金鑰是通過D-H演算法計算出來的,不會在網路上傳輸,其破解的難度取決於離散對數的破解難度,一般不會被破解的,有興趣的可以自行了解該演算法原理。

下面我將貼出Key Exchange Init傳送的請求包資料分析

NO. 描述 解釋
1 kex_algorithms 金鑰交換演算法,裡邊即包含我們使用的D-H演算法,用於生成會話金鑰
2 server_host_key_algorithms 伺服器主機金鑰演算法,可以採用 ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,有公鑰和私鑰的說法,公鑰即我們上面講到的pub key,對於公鑰私鑰的概念,可以參見understanding public key private key concepts
3 encryption_algorithms_client_to_server 對稱加密演算法,常用的有aes128-cbc,3des-cbc
4 mac_algorithms_client_to_server MAC演算法,主要用於保證資料完整性
5 compression_algorithms_client_to_server 壓縮演算法
  • 4、認證階段

上圖序號(678-680)即是SSH版本認證階段。

  • 1、基於賬號和口令的驗證方式

    客戶端將自己的使用者名稱 + 密碼用上面生成的會話金鑰key進行加密之後傳送到伺服器端進行驗證,伺服器端驗證通過,則響應成功,否則在進行有限次(推薦是20次)重新認證。至於伺服器是怎麼驗證的,是否結合了會話ID小編也不清楚,網上眾說紛紜。
    注意,使用者名稱和密碼是採用上面金鑰協商階段生成的會話金鑰key進行加密的,包括後面的連線會話階段所傳送的資料都是,不要認為是採用伺服器的pub key加密的

  • 2、基於公鑰和私鑰的驗證方式

    這種方式也稱為免密登陸。簡單地說,就是客戶端自己生成公鑰私鑰(通常採用ssh-keygen程式生成),然後將公鑰以某種方式(通常是手動新增)儲存到伺服器~/.ssh/authorized_keys檔案中,以後伺服器都會接受客戶端傳過來的經過會話金鑰加密過的公鑰,然後解密得到公鑰之後和本地authorized_keys配置的公鑰是否相等,如果是,則允許登陸。

如果你配過github或者gitlab的公鑰,其實第二種方式認證方式很好理解,因為github它們也是採用SSH協議進行程式碼克隆的,沒有配置公鑰好像是不允許克隆的。

三、相關問題

  • 1、金鑰協商階段安全嗎?有沒有中間人攻擊的情況!

就我的理解,第一次總是不安全的。為什麼呢?上面說到協商過程中,伺服器會將自己的公鑰server_host_key_algorithms傳送給客戶端,但是客戶端無法保證它拿到的公鑰就是目標伺服器所發出來的,很可能有個中間人攔截了你的請求,然後中間人發了另外一個公鑰給到你客戶端,這就不安全了!這也很好解釋了為什麼我們第一次登陸的時候,Shell終端總是會出現這個提示的原因:

這也是將確認權留給客戶端自己去判斷的一種策略。相反,如果想要更加安全,那麼我們可以採用第二種認證方式進行登陸。由於提示的是經過MD5之後的公鑰,那麼我們怎麼判斷這個值是有效的呢?請看下面第3個疑問。

  • 2、傳輸協議協商出來的會話金鑰和會話ID到底有什麼作用?

會話金鑰:對稱加密演算法的金鑰,用於對通訊資料進行加解密,會話ID有點像WEB中的Session,就是用來表示每一個會話的,同時在認證階段也起判斷是否同一會話有效的作用。

  • 3、既然密碼驗證登陸,那麼客戶端第一次登陸的時候如何驗證伺服器公鑰的正確性?

請你告訴小編吧!小編苦於找不到答案!

四、其他

下面是相關資料文件:

我們可能會問了,既然有了SSH協議文件,那麼假如我們想要照著文件寫一個實現出來,那麼應該怎麼去入手呢?其實SSH中的傳輸協議[SSH-TARNS]是基於TCP協議的,因此我們可以從建立一個最基本Java Socket套接字開始,逐步實現SSH的傳輸協議、認證協議以及連線協議。如果你對實現過程感興趣,可以研究下ganymed-ssh2-build209.jar中的原始碼,結合SSH協議RFC文件,你就會發現,其實SSH協議中的協商和認證過程,其實都是通過Socket輸入流、輸出流的形式實現的。小編自己擼到協商階段果斷放棄,原因是不懂得演算法太多了!

相關文章