# 如何在 Windows 架設高安全性的 SFTP (SSH File Transfer Protocol) 伺服器

延續的我前一天的 [如何在 Windows 正確的安裝與設定 OpenSSH Server 服務](/post/2021/12/11/How-to-setup-OpenSSH-Server-in-Windows) 文章，在 Windows 平台架設 OpenSSH Server 搭配 SFTP 就能進行安全的檔案傳輸，防火牆也只需要開啟 Port 22 而已，清楚明瞭，系統配置預設也非常安全。這篇文章我就來分享如何對 SFTP 進行安全的設定，讓你在遠端檔案傳輸時能夠更加安全。

### 設定金鑰免密碼登入

Windows 版本的 OpenSSH Server 只能接受兩種認證方式 ([AuthenticationMethods](https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration?WT.mc_id=DT-MVP-4015686#authenticationmethods))：

1.  Password-based authentication (`password`)
2.  Key-based authentication (`publickey`)

密碼驗證預設就是啟用的，而金鑰驗證則要特別設定 [AuthorizedKeysFile](https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration?WT.mc_id=DT-MVP-4015686#authorizedkeysfile) 而且還要有正確的 NTFS 權限設定才能生效。詳見 [Fix SSH file permissions](https://github.com/PowerShell/Win32-OpenSSH/wiki/Security-protection-of-various-files-in-Win32-OpenSSH) 文章說明。

以下就是一個完整的設定過程步驟：

1.  我們先在 Windows 主機建立一個帳號名稱為 `user1` 的使用者
    
    你可以使用 `net user` 命令快速建立使用者：
    
    ```powershell
    net user user1 * /ADD
    ```
    
    > 過程中需要設定 `user1` 帳號的密碼，需輸入兩次。
    
2.  嘗試從登入這台 Windows 的 OpenSSH Server
    
    我們可以用 `ssh` 命令直接登入 `user1` 使用者，預設會提示你要輸入密碼，這是 Windows OpenSSH 內建的 `password` 認證方法：
    
    ```bash
    ssh user1@localhost
    ```
    
    如果你想登入 AD 網域帳號，可以選擇以下任何一種語法：
    
    ```powershell
    ssh -l user@domain host
    ssh domain\user@host
    ssh user@domain@host
    ```
    
    > 若要以 Kerberos Authentication 登入，可以參考[這裡](https://github.com/PowerShell/Win32-OpenSSH/wiki/ssh.exe-examples#login-using-kerberos-authentication)。
    
    首次登入成功時 Windows 會自動建立 `C:\Users\user1` 這個所謂的 **使用者設定檔** (`User Profile`) 資料夾，在還沒有任何登入紀錄前，是沒有這個目錄的。但在第一次登入成功後，預設就會啟動 OpenSSH Server 設定好的 `DefaultShell` (預設殼層)，讓你可以進行遠端操作：
    
    ![image](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/373720a5df74eb787231-145670365-b7cacfa3-e07e-4562-8a31-782e759cadc9.png)
    
3.  建立 `.ssh` 資料夾
    
    只要是全新的使用者，我通常會在第一次遠端登入後立刻建立好一個 `.ssh` 資料夾：
    
    ```bash
    mkdir .ssh
    ```
    
    接著就可以輸入 `exit` 登出 Windows 遠端殼層環境。
    
    **我們也可以在第一次 SSH 連線時就直接建立 `.ssh` 資料夾，底下命令可以一口氣建立好 `User Profile` 資料夾與相對應的 `.ssh` 資料夾**：
    
    ```bash
    ssh user1@localhost mkdir .ssh
    ```
    
    > 上述命令等於遠端登入 OpenSSH Server 並執行完 `mkdir .ssh` 之後自動登出。
    
4.  建立 `.ssh\authorized_keys` 授權金鑰檔
    
    如果你想從一台 Windows 的 `ssh` 使用 `publickey` 無密碼遠端登入到另一台 Windows 的 OpenSSH Server，那麼你需要先學會如何透過 PowerShell 建立 SSH 金鑰組。命令很簡單，底下這一行就可以搞定：
    
    ```powershell
    ssh-keygen -t rsa -b 4096 -f $HOME/.ssh/id_rsa -N '""'
    ```
    
    > 注意：Windows 上的 `ssh-keygen` 的 `-N` 參數必須傳入 `'""'` 才能正確的產生出**無密碼**的私密金鑰，這很雷！
    
    ```powershell
    # 複製你的公開金鑰到遠端的 .ssh/authorized_keys 檔案中 (此命令會覆蓋此檔案)
    scp $HOME/.ssh/id_rsa.pub 'user1@localhost:.ssh\authorized_keys'
    ```
    
    > PowerShell 預設就有一個跟 Linux 完全一樣的 `$HOME` 環境變數喔！
    
    如果你想從一台 Linux 的 `ssh` 使用 `publickey` 無密碼遠端登入到另一台 Windows 的 OpenSSH Server，那麼你需要這樣建立 SSH 金鑰組：
    
    ```powershell
    ssh-keygen -t rsa -b 4096 -f $HOME/.ssh/id_rsa -N ''
    ```
    
    ```bash
    # 複製你的公開金鑰到遠端的 .ssh/authorized_keys 檔案中 (此命令會覆蓋此檔案)
    scp $HOME/.ssh/id_rsa.pub 'user1@172.23.64.1:.ssh\authorized_keys'
    ```
    
    > 註1: 上述命令皆從 Linux 的 bash 環境下執行，路徑中有 `\` 的話，命令列參數一定要用單引號 `'` 框起來，否則路徑會有異常！
    
    > 註2: 若 Windows 主機上的 `.ssh\authorized_keys` 檔案有自己以外的一般使用者可讀取的話，該檔案就會自動失效，這是 OpenSSH Server 預設的安全機制，所以請注意該檔案的 NTFS 檔案權限設定。詳見 [Fix SSH file permissions](https://github.com/PowerShell/Win32-OpenSSH/wiki/Security-protection-of-various-files-in-Win32-OpenSSH) 文章說明。
    
    > 註3: 如果要在遠端殼層直接編輯文字檔，我會建議你安裝 Windows 版的 [vim](https://community.chocolatey.org/packages/vim) 來用！(`choco install vim -y`)
    
5.  接著你就可以免密碼登入了，安全又可靠！
    
    ```powershell
    ssh user1@localhost
    ```
    
    若是在 CI/CD 的環境下連線，我們可能會需要透過 `-i` 指定自訂的**私密金鑰**，此時你可以這樣執行：
    
    ```powershell
    ssh -i .\id_rsa user@host
    ssh -i .\id_rsa -l user@domain host
    
    echo 'put -r shares/' | sftp -i .\id_rsa user@host:.
    ```
    

### 設定 `sshd_config` 的 `ChrootDirectory` 參數

我在設定 Windows 的 SFTP 支援時，通常會啟用 OpenSSH Server 的 `ChrootDirectory` 設定，將使用者透過 SFTP 登入時，限制在特定目錄下。這個設定在 Linux 設定 SFTP 的時候很常用，而 Windows 版的 OpenSSH Server 是從 `v7.7.0.0` 版 (2018/5/31)開始支援這個設定，如果你使用 Windows Server 2019 的 LTS 版本，記得要安裝最新版的 OpenSSH Server 才能正常使用。

通常我們會針對「特定使用者」不允許遠端登入，並且限制該使用者只能透過 SFTP 存取「特定資料夾」下的檔案，以增強檔案傳輸的安全性。

以下是設定步驟：

1.  建立要讓 `user1` 使用者透過 SFTP 上傳/下載檔案的資料夾
    
    你可以在 Windows 主機上設定任意一個資料夾，只要該資料夾的權限可以讓 `user1` 可讀或可寫即可。
    
    ```powershell
    mkdir C:\Files\user1
    ```
    
    > 如果你直接用 `C:\Files\user1\Downloads` 目錄的話，就不用特別建立，可以直接使用。
    
2.  編輯 `$env:ProgramData\ssh\sshd_config` 檔案
    
    你只要從 Windows 開啟 `$env:ProgramData\ssh\sshd_config` 檔案，在該檔案最下方加入以下設定即可：
    
    ```
    Match User user1
      AllowTcpForwarding no
      ChrootDirectory C:\Files\user1
      ForceCommand internal-sftp
    ```
    
    這裡的 `Match User user1` 就是只針對單一使用者 `user1` 進行設定，最後的 `ForceCommand internal-sftp` 就是為了讓該 `user1` 使用者無法遠端登入主機，只能使用 SFTP 傳檔而已。這裡最重要的當然就是 `ChrootDirectory` 設定，他會把使用者「關在」特定一個目錄下，遠端透過 SFTP 操作檔案時，是完全看不到 Windows 電腦中的任何其他資料夾！ 👍
    
3.  重新啟動 `sshd` 服務
    
    ```powershell
    Restart-Service sshd
    ```
    

### 測試 sftp 上傳檔案

-   將目前資料夾所有檔案上傳到遠端預設目錄
    
    ```batch
    echo 'put -r *' | sftp user1@localhost:.
    ```
    
    > 這個命令會連同所有子目錄一併上傳到遠端電腦。
    
-   將目前資料夾下的 `shares/` 資料夾中所有檔案上傳到遠端預設目錄
    
    ```powershell
    echo 'put -r shares/' | sftp user1@localhost:.
    ```
    
    > 注意: 上傳到遠端的目錄下，不會有個 `shares` 資料夾，而是只有來源端 `shares/*` 底下所有內容。
    
-   批次上傳作業
    
    假設你有一堆已知檔名的檔案要上傳，你可以把所有 sftp 命令都先寫好，並且一次套用上傳。
    
    例如你的 `batch_commands.txt` 命令檔如下：
    
    ```
    cd UploadFiles
    put '046020882904913013298 20211028.pdf'
    put '051725312905913026483 20211029.pdf'
    ```
    
    接著你就可以用以下命令執行批次，sftp 會幫你依序執行所有命令：
    
    ```powershell
    cat batch_commands.txt | sftp user1@localhost
    ```
    

### 相關連結

-   [Win32-OpenSSH Wikis](https://github.com/PowerShell/Win32-OpenSSH/wiki)
    -   [SFTP Usage Examples](https://github.com/PowerShell/Win32-OpenSSH/wiki)
    -   [SSH Usage Examples](https://github.com/PowerShell/Win32-OpenSSH/wiki/ssh.exe-examples)
    -   [Using Certificate Authentication](https://github.com/PowerShell/Win32-OpenSSH/wiki/Certificate-Authentication)
    -   [Fix SSH file permissions](https://github.com/PowerShell/Win32-OpenSSH/wiki/Security-protection-of-various-files-in-Win32-OpenSSH)
-   OpenSSH
    -   [OpenSSH Server Configuration for Windows 10 1809 and Windows Server 2019](https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration?WT.mc_id=DT-MVP-4015686)
    -   [OpenSSH key management for Windows | Microsoft Docs](https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_keymanagement?WT.mc_id=DT-MVP-4015686)
