內容簡介:我們經常會使用我們也明白這是使用了SSH協議進行登陸,但我們想知道的是,為什么可以使用SSH協議進行登陸,而且為什么使用SSH就是安全的,其背后的原理是什么?下面我們就一起來探討下這幾個話題。
本文轉載自:https://juejin.im/post/5baaf517e51d453df0442dce,本站轉載出於傳遞更多信息之目的,版權歸原作者或者來源機構所有。
一、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、既然密碼驗證登陸,那么客戶端第一次登陸的時候如何驗證服務器公鑰的正確性?
請你告訴小編吧!小編苦於找不到答案!
四、其他
下面是相關資料文檔:
- 1、 SSH RFC中文文檔地址
- 2、 The Secure Shell (SSH) Protocol Architecture
- 3、 The Secure Shell (SSH) Transport Layer Protocol
我們可能會問了,既然有了SSH協議文檔,那么假如我們想要照着文檔寫一個實現出來,那么應該怎么去入手呢?其實SSH中的傳輸協議 [SSH-TARNS]
是基於TCP協議的,因此我們可以從創建一個最基本Java Socket套接字開始,逐步實現SSH的傳輸協議、認證協議以及連接協議。如果你對實現過程感興趣,可以研究下 ganymed-ssh2-build209.jar
中的源碼,結合SSH協議RFC文檔,你就會發現,其實SSH協議中的協商和認證過程,其實都是通過Socket輸入流、輸出流的形式實現的。小編自己擼到協商階段果斷放棄,原因是不懂得算法太多了!