SCP免密傳輸和SSH登錄協議詳解
在linux下開發時,經常需要登錄到其他的設備上,例如虛擬機內ubuntu、樹莓派等等,經常涉及到傳輸文件的操作,傳輸文件有很多中方法,如物理磁盤拷貝,基於網絡的samba服務、SCP傳輸、ftp文件傳輸等等,今天我們就來聊聊出場頻率最高的SCP傳輸。
SCP的使用
在linux環境中,當我們需要在兩台機器之間傳輸數據時,經常會用到SCP指令,(SCP指令其實就是基於SCP(secure copy)協議實現的應用程序),SCP的使用示例:
scp a.out downey@192.168.1.101:/home/donwey
從示例可以看出,上述示例為:將a.out這個文件傳送給IP為192.168.1.101目標機器的/home/downey目錄下,如果是第一次傳輸,終端會顯示:
The authenticity of host '192.168.4.77 (192.168.4.77)' can't be established.ECDSA key fingerprint is 7f:d8:d6:37:4f:a7:6f:8d:a3:00:45:7f:0d:xx:xx:xx.Are you sure you want to continue connecting (yes/no)?
接着鍵入:
yes
然后系統會提示你輸入賬戶和密碼,注意這個密碼指的是目標機器上用戶的賬號密碼,正確輸入之后傳輸完成。
知道怎么做,當然還需要知道為什么這么做,所以我們需要來探究scp指令背后的秘密。
預備知識
在介紹SCP協議之前,我們先得了解一下SSH協議,和計算機中的加密方式,因為SCP協議的登錄過程基於SSH協議。
對稱加密
對稱加密,即在數據傳輸過程中對數據進行加密和解密的算法使用同一個密鑰,這種加密算法有什么弊端呢?
當客戶端需要與服務器端進行數據傳輸時,不管是由客戶端還是由服務端產生的密鑰,都得將這個密鑰傳給對方才能實現加解密,而在傳輸的過程中就可能造成密鑰的泄漏。
那么有些人就要說了,那我不用網絡傳輸秘鑰,我可以使用口頭傳輸或者u盤拷貝的方法。
是的,這樣就可以保證安全,但是在大多數互聯網應用中,通信雙方是陌生設備,無法做到上述的方式。
非對稱加密
針對對稱加密帶來的風險,非對稱加密則解決了這個問題。
將加解密使用的密鑰分為公鑰和私鑰,公鑰可以公開,而私鑰則由密鑰生成者保存,客戶端使用公鑰進行加密,而服務端用私鑰進行解密,且目前來說還沒有出現能從公鑰推算出私鑰的的破解機制。
所以相對於對稱加密而言,這種加密方式更加安全,但是這種加密方式的缺點是會占用更多的計算機資源,而且這種資源的耗費量是巨大的。
常見的加密方式
對稱加密:
-
DES(Data Encryption Standard):加密速度較快,適合加密大量數據的場合。
-
3DES:基於DES的加密算法,顧名思義,這是對一塊數據用三個不同的密鑰進行三次加密,顯而易見強度更高,更安全。
-
RC2和RC4:用變長密鑰對大量數據進行加密,速度比DES更快
-
IDEA:(International Data Encryption Algorithm)國際數據加密算法,使用 128 位密鑰提供非常強的安全性;
-
AES:高級加密標准,是下一代的加密算法標准,速度快,安全級別高,這個加密算法被廣泛應用。
非對稱加密:
-
RSA:是一個支持變長密鑰的公共密鑰算法,需要加密的文件塊的長度也是可變的,目前使用最廣的加密算法
-
Diff ieˉHellman:每次交換數據的時候都使用一組新的密鑰,有效地防止了第三方獲得私鑰后解密所有信息,但是同時對中間人攻擊的防護比較薄弱。
SSH協議
SSH是一種加密的網絡傳輸協議,可在不安全的網絡中為網絡服務提供安全的傳輸環境,最常見的運用是遠程登錄。SSH的交互流程是這樣的:
-
客戶端發送請求:這個請求其實就是ssh登錄請求,即發起者擁有服務器上的某個用戶的賬號密碼,以遠程登錄的方式來操作服務器。
-
服務器將公鑰發送給客戶端,這個公鑰是給客戶端進行數據加密用的
-
客戶端收到服務器公鑰,確定是否繼續連接?為什么會有這么一個流程呢,既然我指定了就是要連接服務器,為什么還要讓我確認一次,這會不會是多此一舉呢?其實不然,這是為了防范中間人攻擊。
-
客戶端用公鑰對密碼進行加密,再發送給服務器
-
服務器用私鑰對密碼進行解密,返回登錄結果
中間人攻擊
剛剛提到中間人攻擊,我們就來看看什么是中間人攻擊?
我們大可以想一想,在一個不安全的網絡中,如果我發送的請求被第三方截獲,而第三方把他的公鑰發給我,而我用他的公鑰對密碼進行加密,然后再發送給他,他就獲得了完整的賬號密碼,這時候他就可以拿着從我這兒竊取來的信息登錄服務器。
那么,在一個不安全的網絡中,我們該怎樣防范這種攻擊方式呢?一般有兩種方法:
-
服務器將公鑰公布在官方或者以其他方式公開,讓用戶可以查到進行對比,如果不匹配,就在第三步的時候取消登錄即可。
-
CA證書,由一個權威機構CA對公鑰進行認證,CA機構的數字簽名使得攻擊者不能偽造和篡改證書。CA機構會為申請者發放一個公鑰,CA將該公鑰和申請者的身份信息綁定在一起,並為之簽字。
如果一個用戶想鑒別另一個證書的真偽,他就用 CA 的公鑰對那個證書上的簽字進行驗證,一旦驗證通過,該證書就被認為是有效的。
SCP的傳輸
說回到SCP的傳輸,SCP的傳輸第一步就是進行SSH的登錄動作,然后再基於SSH協議進行文件的傳輸,整個傳輸過程是加密的。
在上面提到的ssh登錄流程中,是最基本的登錄流程,特點在登錄時服務器要求客戶端提供賬號密碼進行驗證,其實這樣是非常麻煩的,在每次登錄或者傳輸文件時,都要輸入賬號密碼,這對於以偷懶為終極目標的程序員來說這是不能忍受的。
SCP的免密傳輸
我們可以想一想,為什么在每次登錄的時候都需要用戶輸入密碼?
所有人都知道這是為了驗證登錄者的是否有操作權限,那換一個思路想一下,有沒有另外一種方法也能驗證登錄者有這個登錄權限呢?
當然是有的,這種驗證方式建立在非對稱加密協議上,當客戶端C想要訪問服務器S時,在客戶端生成一對密鑰(通常使用rsa協議),將客戶端C生成的公鑰放置在服務器S上,這樣在連接時只需要驗證服務器S上是否存在客戶端C的公鑰就可以完成驗證操作。(詳細流程見下文)
實現SCP免密傳輸的操作
-
如果你在服務器擁有一個賬號,名為downey
-
在某一時刻你需要用一台客戶機A登錄到服務器上進行操作
-
在客戶機A的家目錄下的.ssh目錄下生成一對秘鑰,linux下的指令為:
ssh-keygen -t rsa -P ""
(ssh-keygen為秘鑰生成應用程序,-t rsa 表示加密類型為rsa) -
執行上述命令后,系統會提示選擇生成秘鑰放置的目錄和密碼,一般為默認選擇,一路回車。(但是如果你要追求更高的安全性,你可以選擇添加上私鑰密碼),然后在/home/downey/.ssh目錄下就會生成兩個文件:id_rsa和id_rsa.pub,其中id_rsa為私鑰,而id_rsa.pub為公鑰。
-
在服務器端的/home/downey/.ssh/目錄下新建文件authorized_keys(如果有則不用創建),將客戶端A上剛剛生成的id_rsa.pub文件中的內容復制到服務器端 /home/downey/.ssh/authorized_keys文件中(如果里面有內容則另起一行)
這樣就可以直接用scp指令(或者ssh登錄)進行文件傳輸而不用進行賬號密碼驗證了,相當於客戶機(操作機器)與服務器建立了安全連接(原理見下文),如果換一台機器登錄,依舊需要驗證(因為服務器端的公鑰只與這台機器上私鑰相匹配)。
注意
在這些文件操作的時候,特別需要注意的就是文件的權限問題,很多朋友就是把文件從其他地方復制過來,然后修改文件確保文件內容與目標一致就可以,但是發現這樣的操作並沒有達到免密傳輸的效果,很可能就是文件權限出了問題
同時,在某些服務器平台上,可能會提供網頁端的接口來存放客戶端的pub_key,就像github添加公鑰的操作方式,但是原理都是將客戶端公鑰添加到服務器中
免密傳輸的登錄流程
實現SCP傳輸的第一步是通信雙方進行安全連接(登錄),上面提到的SSH登錄流程為SSH首次登錄的大概講解,我們不妨來詳細探究在免密傳輸時的登錄流程:
-
客戶端向服務端發送連接請求,詢問服務器是否支持pubkey的方式進行登錄
-
服務端收到客戶端的請求,表示接收pubkey的方式進行登錄。
-
接收到服務端的回復,客戶端決定使用pubkey的方式進行登錄,客戶端將一段數據用私鑰進行加密,生成簽名,並且將自己的公鑰發送給服務器。
-
服務端收到客戶端發過來的數據,首先將客戶端的公鑰取出來,在/home/$USER/.ssh/authorized_keys/中查找是否存在客戶端的公鑰,如果有,進行對比。
僅僅對比是否存在客戶端的公鑰當然是不夠安全的,服務器接着使用客戶端提供的公鑰對客戶端發過來的簽名(經私鑰加密)進行解密,如果解密后的數據內容正確,表示整個驗證流程完成。 -
服務端返回登錄結果。
SSH登錄流程疑難解答
客戶端發送給服務器的簽名數據
在上述免密傳輸的登錄部分的第三點我有提到,客戶端將一段數據用私鑰加密生成簽名,然后服務器再對這段數據進行解密,那么服務器怎么知道這段數據是否正確呢?
首先,客戶端發送給服務器的數據字段是這樣的:
byte SSH_MSG_USERAUTH_REQUEST
string user name
string service name
string "publickey"
boolean TRUE
string public key algorithm name
string public key to be used for authentication
string signature //數據簽名部分
而客戶端使用私鑰進行加密的數據字段是這樣的:
string session identifier
byte SSH_MSG_USERAUTH_REQUEST
string user name
string service name
string "publickey"
boolean TRUE
string public key algorithm name
string public key to be used for authentication
對比發現,事實上客戶端同時發了一份數據和數據的簽名給服務端,服務端就可以做對比了。
為什么公鑰可以對私鑰加密的數據解密
很多朋友看完上述免密傳輸登錄流程之后,表示非常疑惑:
為什么公鑰可以對私鑰加密的數據進行解密?
公鑰本來就是公開的,那豈不是私鑰加密的數據根本沒有安全性可言?
首先回答第一個問題:為什么公鑰可以對私鑰的加密數據進行解密。答案是:這是SSH協議的規定(好像是廢話)。
好吧,正經地回答第二個問題,公鑰是公開的,所有人都可以獲得,而私鑰只有擁有者才有,那由這對密鑰加密的數據是否就沒有安全性可言呢?
答案當然不是,安全性高着呢。只是,一般的加密過程是公鑰加密,私鑰解密,這部分數據自然是安全的。
那么私鑰加密的數據呢,私鑰加密的數據確實沒有安全性可言,因為用私鑰加密數據根本就不是為了安全性,而是作為一種驗證身份的手段。
因為私鑰是唯一的,一旦使用公鑰對私鑰加密的數據解密成功,服務端S就可以完全確定這條消息是由擁有對應私鑰的客戶端C發出的,而不是第三方偽裝者發出的。
所以,公鑰加密私鑰解密,是為了數據的安全性,而私鑰加密公鑰解密,則是為了驗證數據發送者的身份。
數據傳輸的加密方式
一般情況下SSH使用rsa加密算法登錄,SCP基於SSH協議,所以很多人自然地認為在數據傳輸時,使用的加密算法也是rsa。
事實上,由於非對稱加密算法計算太過於耗時,對所有數據進行加密根本不現實,所以在傳輸數據時一般使用對稱加密算法。
上面有提到,對稱加密算法的弊端是密鑰傳輸過程容易被竊取,所以通常使用非對稱加密算法來傳輸對稱加密算法的密鑰,來保證安全性。
為此,我還使用wareshark工具試圖梳理整個數據傳輸流程,花了一天的時間得出一個結論:針對整個SCP實現的加密算法這一塊,以博主的水平,暫時惹不起....(不過也只是暫時!!)
不過收獲還是有的:SCP數據傳輸流程結合了多種加密算法,同時還根據不同級別的安全要求結合不同的加密算法。
一般來說,綜合對稱和非對稱加密算法的特性,非對稱加密一般用於登錄驗證、密鑰傳輸等數據量小、安全性要求較高的的部分。
而對稱算法用來傳輸大量數據。
網上的錯誤描述
事實上,網絡上對於SSH的登錄過程描述有不少錯誤之處,最經典的一個錯誤觀點是服務器發送一個通過公鑰加密的字符串給客戶端,客戶端私鑰進行解密,然后發給服務端。
服務端將發出的字符串與收到的字符串進行對於,完成驗證過程。
這種做法看起來是非常合理的,但事實上SSH並不采用這種做法,具體原因引用官方文檔中的一句話:
the signing operation involves some expensive computation.
To avoid unnecessary processing and user interaction。
這句話意思很簡單,加密的計算操作是非常耗時的,我們應該避免不必要的交互以節省資源。
為什么我那么自信確定它們的描述有誤呢?並不是飄柔給我的自信,而是官方文檔
(多嘴一句,找資源一定要先找官方文檔的)
github的免密傳輸
用過github的盆友可能對免密傳輸有種熟悉的感覺,沒錯!在github的配置中,如果需要進行免密傳輸,流程是:
-
在客戶機生成一對秘鑰,同樣是id_rsa,id_rsa.pub
-
將id_rsa.pub里的內容添加到github的settings的SSH and GPG keys選項頁面中
同樣的,相當於github服務端與機器已經建立用戶識別機制,不需要再用賬號密碼來識別。
好了,關於SCP傳輸的討論就到此為止啦,如果朋友們對於這個有什么疑問或者發現有文章中有什么錯誤,歡迎留言
原創博客,轉載請注明出處!
