# Linux (八) - SSH 安全加密連線 ![SSH](https://www.yan-wyb.com/assets/images/miscellaneous/ssh.jpg =320x200) SSH 全名是 Secure Shell (安全外殼協定),是一種加密的網路傳輸協定,可以在不安全的網路中提供安全的網路傳輸環境。SSH 透過在使用者和客戶端之間建立一條安全的隧道來實現連接,並進行資料的傳輸。SSH 現今最常被使用的是進行遠端連線執行命令。 <!-- more --> ## 加密方式 SSH 採用 `Public-key cryptography` 來為資料進行加解密,每一個使用者都會有兩把鑰匙 (key) : * 公鑰 (public key) : 任何人都可以看到公鑰的內容,主要用於加密。 * 私鑰 (private key) : 私鑰只有擁有者自己擁有不會傳給別人,主要用於解密和數位簽章。 通過公鑰將資料進行加密後,可以很輕易的用私鑰解密,而如果想要用公鑰來猜私鑰就會非常困難。這就是所謂的 `非對稱式加密`,透過不同的兩把鑰匙來分別作加解密,又因為私鑰握在自己手上所以不用擔心在傳輸過程中被攔截。 ### 數位簽章 前面提到公鑰是任何人都可以看到的,那這樣每個人都可以用這把公鑰將資料加密再傳給同一個人,這樣要如何確定這封訊息是誰傳給我的 ? 是否是偽造的訊息呢 ? 因此數位簽章的概念是先將資料透過雜湊函數產生雜湊值,接著再用自己的私鑰將雜湊值加密,並且連同原本加密的資料一起傳送。產生雜湊值的動作是單向的,所以不用擔心會被反向破解出原本的資料。 當對方收到加密資料和數位簽章之後就可以用我的公鑰解出雜湊值,接著再用他自己的私鑰解出加密的資料。此時再將解密後的資料透過雜湊函數產生雜湊值,並和數位簽章解出的雜湊值進行比對,如果一樣則代表這則訊息是我本人傳送的。 ### 運作流程 1. 在進行資料傳輸前, A 和 B 雙方都要先生成一組公私鑰。 2. 將公鑰傳給對方,此時 A 有自己的私鑰和 B 的公鑰,B 有自己的私鑰和 A 的公鑰。 3. 現在當 A 要傳資料給 B 就用 B 的公鑰將資料加密,並且用 A 自己的私鑰將資料進行數位簽章,接著將加密資料和數位簽章一起傳給 B。 4. 當 B 收到後先透過 A 的公鑰解出數位簽章,接著再用 B 自己的私鑰解出加密的資料。 5. 比對數位簽章的雜湊值和解密的資料經過雜湊函數所產生的雜湊值是否相同即可確認資料的真實性。 ### 中間人攻擊 (Man-in-the-middle attack) 雖然看起來上述加了數位簽章之後安全性已經提高了許多,但是公鑰在傳輸的過程中如果在中途被駭客攔截,那駭客就可以發送一組假的公鑰給對方並且解開資料進行偽造。如下 : 1. A 生成了一組公私鑰,並且準備將公鑰傳送給 B。 2. A 傳送的過程中被駭客 H 在中途攔截,此時駭客 H 就再生成了一組 `公私鑰 (pubH1、priH1)` 並將這組 `假的公鑰 (pubH1)` 傳送給 B。而 B 就以為 `pubH1` 是 A 的公鑰。 4. 同樣 B 生成公私鑰後要傳送公鑰給 A 時在中途被駭客 H 攔截,駭客 H 就再生成了一組 `公私鑰 (pubH2、priH2)` 並將這組 `假的公鑰 (pubH2)` 傳送給 A。而 A 就以為 `pubH2` 是 B 的公鑰。 5. 現在當 A 要傳送訊息時用了 `假的 B 公鑰 (pubH2)` 加密資料再用 A 自己的私鑰加密數位簽章,接著準備傳送給 B。 6. 傳送過程中被駭客 H 在中途攔截,此時資料因為是用 `pubH2` 加密,而駭客 H 因為擁有 `priH2` 所以就可以將加密的資料進行解密。 7. 此時駭客 H 就可以任意更改資料內容,並且再用 `B 的公鑰` 將資料進行加密,接著透過 `priH1` 這組私鑰進行數位簽章,最後就可以將這組偽造的資料傳送給 B。 8. 當 B 收到加密的資料和數位簽章時,會先用自己的私鑰將資料進行解密,這部分是沒有問題的因為駭客 H 是用 `B 的公鑰` 進行加密。接著 B 會再用他以為是 A 的公鑰的`pubH1` 解出數位簽章,最後比對結果吻合就認定是 A 傳送的訊息。 ### 第三方認證公鑰 非對稱式協議唯一會產生的問題就是交換公鑰時有可能被偷換過,不過解法其實也很簡單,就是透過一個可信的第三方服務來認證拿到的公鑰是不是真的,這樣就可以確定公鑰有沒有問題。 ## 基本操作方式 如果要使用 SSH 連線到另一台 Linux 伺服器,可以透過 SSH 指令進行連接,如下 : ```bash= ssh [user]@[host] ``` user : 要登入的帳號名稱。 host : 遠端主機的名稱或 IP 位址。 登入時預設要使用帳號的密碼進行認證才能登入。 **範例** 下面這個範例是要連線到 `192.168.0.100` 這個 IP 位址的主機並且用 `tony` 這個帳號進行登入。 ```bash= $ ssh tony@192.168.0.100 ``` 而如果本機的使用者帳號和遠端登入的是同一個帳號名稱則可以不需要指定 `user`,如下 : ```bash= tony@mycomputer$ ssh 192.168.0.100 ``` ### Port SSH 預設是使用 Port `22`,但是不一定要使用 `22`,是可以更改的。許多的伺服器因為安全性因素會改用其他 Port。更改過 Port 會比較安全,預設的 `22` 因為大家都知道,所以必然是成為駭客首先攻擊的目標。 若不是使用預設的 `22` 作為 SSH 的 Port,則可以使用 `-p` 來指定。 ```bash= ssh -p [port number] [user]@[host] ``` **範例** 下面這個範例是已經將 Port 從預設的 `22` 切換到了 `2022`,這樣就可以透過 `2022` 來連線。 ```bash= ssh -p 2022 tony@192.168.0.100 ``` 更改 Port 的方式請參考 [CentOS/Debian/Ubuntu Linux修改SSH默認22端口](https://www.vpser.net/security/centos-debian-ubuntu-linux-change-ssh-port.html)。 ### 遠端執行指令 如果只是要執行一個指令的話不一定要登入伺服器再執行,可以將指令放在 ssh 指令末端就可以直接執行,執行完後會自動離開。 ```bash= ssh [user]@[host] [command] ``` **範例** 這個範例首先先遠端建立一個檔案,就著再次用遠端執行的方式列出來看剛才建好的檔案。 ```bash= $ ssh tony@192.168.0.100 touch test.txt $ ssh tony@192.168.0.100 ls -l total 0 -rw-rw-r--. 1 opc opc 0 Oct 22 03:45 test.txt ``` ### 除錯模式 在 SSH 連線時可以加上 `-v` 開啟除錯模式就會輸出詳細的偵錯訊息。 ```bash= ssh -v [user]@[host] ``` **範例** 這個範例我們加上 `-v` 開啟除錯模式,可以看到會輸出許多的偵錯訊息,就可以利用這些訊息來進行除錯。 ```bash= $ ssh -v tony@192.168.0.100 OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 26 Jan 2017 debug1: Reading configuration data /home/tony/.ssh/config ... 略 ... debug1: Next authentication method: publickey debug1: Trying private key: /home/tony/.ssh/id_rsa debug1: Authentication succeeded (publickey). Authenticated to 192.168.0.100 ([192.168.0.100]:22). debug1: channel 0: new [client-session] debug1: Requesting no-more-sessions@openssh.com debug1: Entering interactive session. debug1: pledge: network debug1: client_input_global_request: rtype hostkeys-00@openssh.com want_reply 0 debug1: Sending environment. debug1: Sending env LANG = en_US.UTF-8 Last login: Thu Oct 22 05:55:44 2020 from 192.168.0.99 ``` ## 免密碼登入 前面有介紹了公私鑰的概念,但是預設登入時還是需要輸入帳號的密碼來驗證。實際上也是可以建立一組公私鑰來代替密碼進行登入,而且只要第一次驗證過了以後就不需要再輸密碼就可以自動驗證並登入。 ### 建立公私鑰 要建立一組新的公私鑰可以透過 `ssh-keygen` 指令完成。 ```bash= ssh-keygen ``` 建立好的公私鑰如果沒有特別指定會放在 `/home/[user]/.ssh` 目錄下,並命名為 `id_rsa` 和 `id_rsa.pub`,分別是私鑰和公鑰。 如果不放心還是希望要輸入一些認證資訊的話可以在 `passphrase` 指定,則連線時就要輸入指定的 `passphrase` 才能登入。 **範例** 下面就直接建立了一組公私鑰,在第三行可以指定建立好的公私鑰要放在哪,在第五行的地方可以指定 `passphrase`。如果都不指定就直接按 `Enter` 略過。 ```bash= $ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/home/tony/.ssh/id_rsa): Created directory '/home/tony/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/tony/.ssh/id_rsa. Your public key has been saved in /home/tony/.ssh/id_rsa.pub. The key fingerprint is: SHA256:EL087cZT+lk4Ha1rU4OdrO0YJrcnM8+u6gvifvYOPhM sample\tony@mycomputer The key's randomart image is: +---[RSA 2048]----+ | .. | | .. | | .. o . | | .+ . . . .| | S+ o o+o.| | E* o.+=.| | . +.+ Boo.| | . o=o *=Oo | | .oo.**o=X* | +----[SHA256]-----+ ``` 下面分別就是產生出來的私鑰和公鑰,格式就長這樣,內容有稍微調整過了所以不要直接拿去用會沒有用。請自己建一組出來。 ```bash= $ cat id_rsa -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAxpp1bTuxB2mv8+Nv0hlwzDAO1OaAS926F2TtvP1J0DJw/hIG BIYOI1i9S7PvzsEKg0fjqIF7K5KwZXGzEF8Wq37zMh4eife72mWFquU63+Di8Cfc ZEqRH2gdM7EVs88BjSNoD/1Djyw642CKPMvCQPeehEdVuQLbuUjVCq679uNLN8BQ 07kc9AOx6Yd16LJf8i5Yx/hepOWKqmYPyvxkZUbkTJmghbq77v4t8asuld+2iKY6 N4+xD602aCLJT0vKfK1lo+wDsEK8/n5nfAKRpY3lGwEm8vuEmsnPLT6+lPWs2Ei4 qjogWcfcz7t+3gfuNJVAVJzJhVlkBA0Ic6/vhwIDAQABAoIBAQCVKAPxGdeGvbVL 8hBVNo4kjCNXehX45HmSgFacjY4WMwvxhQ/H7fZITak9ZdJA8XI3zpIIR9Z9jxmz 1oSpUwDvo9R6W2yMKeaHy59sDeT9pyRJQWagW/BKKiNkYybteBauLV1GROusZ4eC pUqtBkrKCC6hIgIbhS4IJc76yuiYLiC09XcTx54DvJDjNPuQ5nrpd3vQVsZRku8u oxUOo5Iymp/n8xRKoNWIdV2wmo+dC7pqLmXGd8KldVW8xfhc4DEwbnMGnW80nKne pPjYJgH0iMU6Bqg5UclNUZvFoTKW2DDhME/MxxhlBuDdR9Rf2kaJqZtFTmCosCks ZugeODfhAoGBAPVbsRBZTTNVLU/whSF8roIUoCsalAA7XF9AmOQIP32ZwqF8KkIh ynn1DEI5CqARuwL9ZWHGKtEyFR7ARWjzyJ6zDUPJYDvCh08nvuJATB3EoIiVnsLD LZLEZD/A0z/rF5ShmLrKItvHxfed1ACu/JxT527bOpd8TxQcQCp0j2bzAoGBAM83 oUINiYy7Q4p69ySvvnwFnMwgLZjvAuSgxq53fAyl4H7NFz1IDfc9W4N1l/aaeFak RuZbT/sc/FU56gpbgm6hHvWox3WyyyT20F/yF2QTIpylnHdJElDLSvIP1ajFkJQI /pYOEx8kbwFdDJ7T6BCClElFvPSataC/wEcYZyIdAoGBAMc0V7D3YCeSO0TQ+bXz fdedGWGFpa3+hAPN9ZQGYk3+fbgQkFq7BqI342kM2OSXIKfPxfU5KGBKcj1SS8hn TXYT12w23kwGp0cL9P3KyEHO96uNFqX4Q1MK4n2kd59Ipw+El/Tn0p9wNfnRiWO4 rt3ZfWhS0+JUCfxA55PQqPEjAoGAXlfHlZkwPOCtmYjuqMdTBIhRoSYtXGnFeW4x xxEja2bCVTjvEbmSkjRfS/ToISCnFVtp5EtCmOPThsH9aoXLVq8It3QGi/HBR60P pf9E4w1LPuKv4CdmrV5BWwAWmOaKMrv9nTf/QLiHyakhq9Zy28F33fxgUUV22B81 Xakgm4kCgYA5yfqslSLW8jlN7+iMqUE+McBopiKi+dm58R4W5XrcbqLR89qhR/sS TnSTemvoaPnxDhajHIPFMBdeYWLx+U+XivmBqMaJrUr8Q4ImVg7pUD5Kjj9l4S34 Pu9vkqN+G7qQF4yyCdUOp2ZBZpGEPAVb31fWOImwkncGtxy+Sn6tRw== -----END RSA PRIVATE KEY----- $ cat id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGmnVtO7EHaa/z42/SGXDMMA7U5oBL3boXZO28/UnQMnD+EgYEhg4jWL1Ls+/OwQqDR+OogXsrkrBlcbMQXxarfvMyHh6J97vaZYWq5Trf4OLwJ9xkSpEfaB0zsRWzzwGNI2gP/UOPLDrjYIo8y8JA956ER1W5Atu5SNUKrrv240s3wFDTuRz0A7Hph3Xosl/yLljH+F6k5YqqZg/K/GRlRuRMmaCFurvu/i3xqy6V37aIpjo3j7EPrTZoIslPS8p8rWWj7AOwQrz+fmd8ApGljeUbASby+4Sayc8tPr6U9azYSLiqOiBZx9zPu37eB+40lUBUnMmFWWQEDQhzr++H sample\tony@mycomputer ``` ### 複製公鑰到被連線主機 `scp` 指令可以遠端加密複製檔案或目錄。這裡就不詳述 `scp` 指令的用法,下面的指令是將剛剛建立好的公鑰 `~/.ssh/id_rsa.pub` 複製到 `192.168.0.100` 這台主機的 `tony` 這個使用者下的 `~/.ssh/` 這個目錄。 ```bash= $ scp ~/.ssh/id_rsa.pub tony@192.168.0.100:~/.ssh/ ``` ### 將公鑰寫入 authorized_keys Linux 系統預設放置公鑰的檔案是 `authorized_keys`,所以要先登入到遠端的主機或是使用遠端執行命令將公鑰寫入到 `authorized_keys`。 ```bash= $ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys ``` ### 更改 authorized_keys 的權限 如果是之前沒設定過 `authorized_keys` 的權限或是第一次加入公鑰到遠端主機的話都要設定 `authorized_keys` 的權限,若是權限太大則 `ssh` 會判斷金鑰不安全而不能使用。這裡我們設定自己可以讀寫即可。 ```bash= $ chmod 600 ~/.ssh/authorized_keys ``` ### 重新登入 至此再重新登入就可以不需要密碼了,如果還有遇到金鑰權限的問題可以先檢查一下本機的 `id_rsa` 和 `id_rsa.pub` 權限是否過大,可以設定為自己可讀寫就好。 ```bash= chmod 600 ~/.ssh/id_rsa.pub chmod 600 ~/.ssh/id_rsa ``` ## 使用別名登入 從前面介紹的 SSH 連線方式可以看到,每次連線都要輸入帳號和 Server 的 IP 位址,但是如果有多台 Server 要連就可能會記不住。因此可以在 `~/.ssh` 目錄下的 `config` 檔加入一些設定,以後就可以只用別名來進行連線。設定的內容如下 : ```bash= Host [alias] HostName [IP address] Port 22 User [user name] ``` Host : 可以自行設定想要的別名,之後就會用這個名稱進行連線,所以建議是直接用 Server 的名稱。 HostName : 填入 Server 的 IP。 Port : ssh 預設的 Port 是 22,如果你的系統有更改過這裡就需要再特別設定。 User : 要登入的使用者帳號名稱。 設定完成後就可以直接指定別名 (Host) 來進行登入,如下 : ```bash= ssh [alias] ``` 而原本不指定用別名連線的方式就可以對應出如下的格式 : ```bash= ssh [user name]@[IP address] ``` **範例** 首先要先在 `~/.ssh/config` 中加入 Server 的設定,如果 `~/.ssh/config` 不存在,那就建立一個,如下 : ```bash= $ cat > config Host srv1 HostName 192.168.0.100 Port 22 User mike [Ctrl+D] ``` 設定完成後就可以直接指定別名進行登入,如下 : ```bash= $ ssh srv1 key_load_public: invalid format Last login: Tue Oct 13 06:25:00 2020 from 192.168.0.99 ``` ## 遠端主機的公鑰 在前面介紹 SSH 資料傳輸時會將公鑰傳給對方,而使用遠端連線登入時,遠端的主機同樣也會將他的公鑰傳到我們的本機上。儲存的位置就在 `~/.ssh/known_hosts` 這個檔案裡。如下 : ```bash= $ cat ~/.ssh/known_hosts 192.168.0.100 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLbsxiHTQIhXUCUdjx0dJ6lkrVHeOYds3dIR2hhifiyl6RgVWaZNV+262++orkpamalxTuTSIAVHQ2EWywLWExs= ``` 所以當遠端主機重新設定公鑰或者是重灌系統的話,公鑰就會和本機上的不一樣,造成驗證失敗。這時只要將 `known_hosts` 這個檔案刪除讓 `ssh` 重新連線就會自動抓取再寫到 `known_hosts`。 不過如果刪除 `known_hosts` 的話就變成所有遠端主機的公鑰都要重新抓一次,所以如果不希望這樣的話也可以打開 `known_hosts` 直接刪除對應主機的那一行設定即可。 ## Summary 本篇介紹了 SSH 的原理即使用方式,大部分的情況都是用在遠端登入來進行操控。此外加入了免密碼登入和使用別名登入的功能也可以讓我們更方便的進行連線。 ## 參考 [1] [小技巧:實現ssh服務器别名免密碼登錄](https://www.cnblogs.com/Detector/p/9728632.html) [2] [網路安全(1) - 基礎密碼學](https://blog.techbridge.cc/2017/04/16/simple-cryptography/) [3] [ssh公鑰私鑰認證原理](https://www.itread01.com/content/1541400303.html) [4] [你該知道所有關於 SSH 的那些事](https://jennycodes.me/posts/security-ssh) [5] [CentOS/Debian/Ubuntu Linux修改SSH默認22端口](https://www.vpser.net/security/centos-debian-ubuntu-linux-change-ssh-port.html) [6] [Linux 的 scp 指令用法教學與範例:遠端加密複製檔案與目錄](https://blog.gtwang.org/linux/linux-scp-command-tutorial-examples/) ###### tags: `Linux`