1. SSH是什麼
簡單說,SSH是一種網絡協議,用於計算機之間的遠程加密登錄。
SSH 爲 Secure Shell的縮寫,由 IETF 的網絡小組(Network Working Group)所制定,SSH 爲建立在應用層基礎上的安全協議。SSH 是目前較可靠,專爲遠程登錄會話和其他網絡服務提供安全性的協議。利用 SSH 協議可以有效防止遠程管理過程中的信息泄露問題。SSH最初是UNIX系統上的一個程序,後來又迅速擴展到其他操作平臺。SSH安裝容易、使用簡單,而且比較常見,一般的Unix系統、Linux系統、FreeBSD系統都附帶有支持SSH的應用程序包。Windows如果需要使用SSH,可以安裝PuTTY或者Cygwin。最初的SSH是由芬蘭的一家公司開發的。但是因爲受版權和加密算法的限制,現在很多人都轉而使用OpenSSH。OpenSSH是SSH的替代軟件包,而且是免費的,可以預計將來會有越來越多的人使用它而不是SSH。
2. SSH原理
非對稱加密技術
SSH 服務基於非對稱加密(public-key cryptography,也稱公開密鑰加密)技術實現數據加密傳輸。該技術會生成一對數學相關的密鑰,其中一個(公鑰)用於對數據進行加密,而且只能用於加密,而另一個(私鑰)只能用於解密。使用加密密鑰加密後的數據,只能用對應的解密密鑰才能解密。而且,只知道其中一個密鑰,無法計算出另一個。因此,如果公開了一對密鑰中的一個,並不會危害到另一個的祕密性質。通常把公開的密鑰稱爲公鑰(public key),而不公開的密鑰稱爲私鑰(private key)。
一般來說,非對稱加密的應用場景有兩個:
- 公鑰是用來解密的。比如A給B發送消息,B使用A的公鑰驗證A發佈的數據或文件的完整性、準確性,以防止數據篡改。相應的密鑰稱爲數字簽名(數字證書)。
- 公鑰是用來加密的。比如A給B發送消息,A使用B的公鑰給消息加密,以防被劫持後泄漏。SSH服務基於該場景實現。
與對稱密鑰加密相比,非對稱加密的優點在於不存在共享的通用密鑰。由於解密用的私鑰無需發送給任何用戶,所以可以避免密鑰被劫持或篡改。而加密用的公鑰即便被劫持或篡改,如果沒有與其匹配的私鑰,也無法解密數據。所以,截獲的公鑰是沒有任何用處的。
當前,SSH主要採用 RSA 算法(協議 V2 默認算法)和 DSA 算法(協議 V1 僅支持該算法)來實現非對稱加密技術。
3. SSH連接交互過程
SSH連接時,整個交互過程如上圖。,主要可以分爲三個階段
服務端準備階段
服務端在每次啓動 SSH 服務時,都會自動檢查 /etc/ssh/ 目錄下相關密鑰文件的有效性。如果相關文件檢查發現異常,則會導致服務啓動失敗,並拋出相應錯誤信息。 如果文件相關不存在,則會自動重新創建。
默認創建的相關文件及用途說明如下:
root@vultr:~# ll /etc/ssh
-rw-r--r-- 1 root root 553122 Feb 10 2018 moduli # 用於 DH-GEX 算法
-rw-r--r-- 1 root root 1580 Feb 10 2018 ssh_config # SSH 客戶端配置文件
-rw-r--r-- 1 root root 3249 Jul 19 2018 sshd_config # SSH 服務配置文件
-rw------- 1 root root 227 Aug 2 2018 ssh_host_ecdsa_key # DSA 算法私鑰
-rw-r--r-- 1 root root 172 Aug 2 2018 ssh_host_ecdsa_key.pub # DSA 算法公鑰
-rw------- 1 root root 1675 Aug 2 2018 ssh_host_rsa_key # SSH V2版RSA算法私鑰
-rw-r--r-- 1 root root 392 Aug 2 2018 ssh_host_rsa_key.pub # SSH V2版RSA算法公鑰
-rw-r--r-- 1 root root 338 Jul 19 2018 ssh_import_id
非對稱加密協議
當服務器SSH服務啓動,客戶端也安裝了SSH後,就可以進行連接了。
-
客戶端向服務端發送連接請求。
客戶端連接服務器有兩種方式:1. 密碼登錄。2. 密鑰登錄。
客戶端通過SSH工具連接服務端。相關信息通過明文發送,發送的請求包括IP地址和用戶名,服務器識別對應的客戶端公鑰(保存在$HOME/.ssh/authorized_keys
中),找到該公鑰後,服務器通過公鑰加密一段隨機字符串,並使用共享密鑰加密後發送給客戶端。如果未找到公鑰,則需要輸入密碼。 -
服務端返回公鑰信息
根據客戶端所使用的服務協議版本及算法設置,返回相應公鑰信息。比如,默認情況下,客戶端通過 SSH V2 版協議,基於 RSA 算法建立連接,則服務端將ssh_host_rsa_key.pub
文件中的內容返回客戶端。相關信息通過明文發送。 -
客戶端對服務端公鑰信息進行比對和確認
客戶端接收到服務端公鑰信息後,會進行如下比對,並讓用戶對相關信息進行確認。
- 如果是首次連接服務端,客戶端會收到類似如下信息,讓用戶確認公鑰指紋的有效性:
The authenticity of host '192.168.0.1 (192.168.0.1)' can't be established. RSA key fingerprint is c2:49:d9:43:74:d5:ed:bc:28:9b:d2:7b:63:94:cf:bc. Are you sure you want to continue connecting (yes/no)?
- 如果用戶輸入 no ,則連接中斷並報錯(Host key verification failed)。
- 如果用戶輸入 yes,則會將相應的公鑰信息,保存到當前用戶家目錄下 .ssh 目錄內的 known_hosts 文件中。 比如:
cat ~/.ssh/known_hosts
# IP 明文顯示: 192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA3sdlboGEgY9buZpkPuygPw0NxAvmxYd0mc3fo2MgP+JqgFC9/9ZXOgDXKJrjE2HKBieJZSPKGncIh+zLxTvmykeJQBXv7i1GiUjW+H3VY69Ge3AdGfCd+XF+Cvi1e+j18zhHnjSzvIBoNpT5cBWWNbw7mNHCwTb0sHAVUkWR4Ck/LM5/rQ09A+m6BLfZJL8CRNGxKTbyINi6o812S+Cy64WqDs1nTpIXp2Bkcpjclb36bFSs9Z/tWNuJl7A//7HNtxMgFGBnE07Ykvvy8s06DUmkyFy8GcXGBpnfdg9utLodfQLFQnKflCQZ110BpQaCWlWPjU9dc4w3XLJ/XQOP4w== # IP 加密處理: 3efXAZ4sNHcUcHamBy4gDriblc8=|8idBhLq9aLl2sfh4KswMsk4sPFI= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAwS4DE3hok8RCkxYlTxsexNrNa62e05UGSkoP7ie26DDWjG1Aoc74cCsE4is9p7lEfFUYYlAzeYhPqE/yGf5YxRZUOU2IeFI4cEqo8YZr7edVYpgAq2f2J0zMwk1syenD12lmUPkYA4mMB6it3jxXR5k+H0HZh9YA7mRXkiTjlkAMWirBcnUvtKYRv9LRIr3ikUiPy2gfZO291Ae9zuTsWwEtHQxIpiBgk3vwF2gCUFlX9y//IsMjdQq5prk7x3BjXhUorqgJO1gt1VHW8Xxx9Oe50YF1hi9DuE6VXwyh4xfHTmauRQybwsYafdA3HxrA2od6x9l19D9EH7xHAjDa5w==
- 如果服務端因重裝系統等因素導致公鑰指紋出現變化,則會直接導致連接失敗(報錯Host key verification failed),則需要刪除已保存的條目後再重新連接。
- 如果之前已經成功連接,而且公鑰指紋對比一致,則會繼續下一步操作。
-
Server端接收到Client的連接請求後,會在authorized_keys中匹配到Client的公鑰pubKey,並生成隨機數R,用Client的公鑰對該隨機數進行加密得到pubKey®,然後將加密後信息發送給Client。
-
Client端通過私鑰進行解密得到隨機數R,然後對隨機數R和本次會話的SessionKey利用MD5生成摘要Digest1,發送給Server端。
-
Server端會也會對R和SessionKey利用同樣摘要算法生成Digest2。
-
Server端會最後比較Digest1和Digest2是否相同,完成認證過程
後續數據交互過程
後續登錄校驗及正常的數據傳輸,都會通過雙向加密方式進行。相關交互說明如下:
- 如果服務端需要發送數據給客戶端:
- 服務端使用所持有的客戶端公鑰,對需要傳輸的數據進行加密,再發送給客戶端。
- 客戶端收到信息後,使用所持有的自身私鑰解密後獲取數據。
- 如果客戶端需要發送數據給服務端,也是類似的流程:
- 客戶端使用所持有的服務端公鑰,對需要傳輸的數據進行加密,再發送給服務端。
- 服務端收到信息後,使用所持有的自身私鑰解密後獲取數據。
4. SSH相關配置文件
從這個連接過程中,可以看出,主要使用到兩個文件夾下的內容:
~/.ssh/
這裏主要是客戶端連接信息和密鑰存儲的敵方。這個文件夾下的內容可以使用命令ssh-keygen
生成,執行這個命令後,會生成兩個文件,id_rsa
和id_rsa.pub
。第一個是私鑰文件,第二個是公鑰文件。公鑰需要上傳到服務端 ~/.ssh/authorized_keys
中,但是這都是針對當前登錄用戶的。也就是說,只對當前用戶有效。
/etc/ssh/
這個主要是ssh配置和服務器密鑰存儲的地方。由SSH服務啓動時生成。
在連接中的兩個過程:
-
客戶端存儲服務器的公鑰到本地
也就是上面的第二步。服務器返回回來的公鑰在
/etc/ssh
下,比如root@vultr:~# ls /etc/ssh moduli ssh_host_ecdsa_key ssh_host_ed25519_key.pub ssh_import_id ssh_config ssh_host_ecdsa_key.pub ssh_host_rsa_key sshd_config ssh_host_ed25519_key ssh_host_rsa_key.pub root@vultr:~# cat /etc/ssh/ssh_host_ecdsa_key.pub ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDlzdrNE506YkXpPznE4DPPBa0GXQ4U2wxkaVxhIiaJHnaHds30okQeG/auZf1mslwZYmMBs7dN4wL/ERjKGNTY= root@guest
之所以有多組密鑰,是因爲使用了不同的加密算法。
客戶端接收到之後,會存儲在~/.ssh/known_hosts
文件裏,查看這個文件,可以看到有一行與服務器ssh_host_ecdsa_key.pub
內容一致。pyjun@DESKTOP-49M11D1:~/.ssh$ cat known_hosts |1|5HUCN+t1jFIpHxBWvuUcnijkvH8=|GVx2vO71UnftuIF6I7gX3qAo5OY= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDlzdrNE506YkXpPznE4DPPBa0GXQ4U2wxkaVxhIiaJHnaHds30okQeG/auZf1mslwZYmMBs7dN4wL/ERjKGNTY=
-
客戶端將公鑰存儲到服務器中
在請求連接過程中,如果使用密鑰的方式登錄,那麼就需要先把客戶端的公鑰存儲到服務器中。ssh-keygen # 生成當前賬戶的私鑰和公鑰,一路回車即可 ssh-copy-id user@host # 將公鑰傳送到遠程主機host上面
其實,它是將客戶端的
~/.ssh/id_rsa.pub
文件內容追加到服務器文件~/.ssh/authorized_keys
中。~/.ssh/authorized_keys
裏表示本機可以被哪些機器訪問
~/.ssh/known_hosts
裏表示本機訪問過哪些機器
5. SSH相關配置
SSH配置文件有兩個,一個是ssh_config
,一個是sshd_config
。前者是客戶端配置,後者是服務器配置。也就是說,如果本機是作爲客戶端,那麼就修改第一個配置,如果本機是作爲服務器,那麼就修改第二個配置,
一般來說,和密鑰登錄的配置有關的有以下幾個,如果密鑰配置好後無法登錄,嘗試配置以下三項。
RSAAuthentication yes
PubkeyAuthentication yes # 如果爲yes,則無法使用密碼登錄
AuthorizedKeysFile .ssh/authorized_keys # 默認服務端公鑰文件位置
其他
passwordAuthentication no # 只能通過私鑰訪問
6. SSH相關命令
-
常見的登錄命令
# 以用戶名user,登錄遠程主機host $ ssh user@host # 本地用戶和遠程用戶相同,則用戶名可省去 $ ssh host # SSH默認端口22,可以用參數p修改端口 $ ssh -p 2017 user@host
-
生成祕鑰操作
$ ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa # -t:指定生成密鑰類型(rsa、dsa、ecdsa等) # -P:指定passphrase,用於確保私鑰的安全(相當於密碼,可以爲空) # -f:指定存放密鑰的文件(公鑰文件默認和私鑰同目錄下,不同的是,存放公鑰的文件名需要加上後綴.pub) # 然後我們看一下 ~/.ssh 文件夾下的內容 $ ll ~/.ssh -rw-r--r-- 1 root root 1199 Jun 27 23:58 authorized_keys # 保存已授權的客戶端公鑰 -rw------- 1 root root 1675 Jun 27 16:48 id_rsa # 保存私鑰 -rw-r--r-- 1 root root 398 Jun 27 16:48 id_rsa.pub # 保存公鑰 -rw-r--r-- 1 root root 666 Jun 27 17:57 known_hosts # 保存已認證的遠程主機ID
7. SSH安全
中間人攻擊
傳統的網絡服務程序,如FTP、Pop和Telnet在傳輸機制和實現原理上是沒有考慮安全機制的,其本質上都是不安全的。因爲它們在網絡上用明文傳送數據、用戶帳號和用戶口令,別有用心的人通過竊聽等網絡攻擊手段非常容易地就可以截獲這些數據、用戶帳號和用戶口令。而且,這些網絡服務程序的簡單安全驗證方式也有其弱點,那就是很容易受到"中間人"(man-in-the-middle)這種攻擊方式的攻擊。
所謂"中間人"的攻擊方式,就是"中間人"冒充真正的服務器接收你的傳給服務器的數據,然後再冒充你把數據傳給真正的服務器。服務器和你之間的數據傳送被"中間人"一轉手做了手腳之後,就會出現很嚴重的問題。中間人能夠攻擊,主要原因在於他能認識截取的信息,也能發出讓接受者認識的信息。
使用SSH,你可以把所有傳輸的數據進行加密,這樣"中間人"這種攻擊方式就不可能實現了,而且也能夠防止DNS欺騙和IP欺騙。使用SSH,還有一個額外的好處就是傳輸的數據是經過壓縮的,所以可以加快傳輸的速度。SSH有很多功能,它既可以代替Telnet,又可以爲FTP、Pop、甚至爲PPP提供一個安全的"通道"。
其實known_hosts這個文件可以通過Client和Server的雙向認證,從而避免中間人(man-in-the-middle attack)攻擊,每次Client向Server發起連接的時候,不僅僅Server要驗證Client的合法性,Client同樣也需要驗證Server的身份,SSH client就是通過known_hosts中的host key來驗證Server的身份的。