﻿# 如何在 PowerShell 對機敏字串進行加解密處理

我經常撰寫許多 PowerShell 腳本，盡量把我日常的工作自動化，這當中經常需要登入一些服務，所以在我的腳本中經常會出現密碼等敏感資料，這些資料如果直接寫在腳本中，通常會有一些風險，所以我通常會對這些敏感資料加密處理，確保這些密碼不會被旁人一眼看穿。這篇文章我就來說說我是怎樣處理這些敏感資料的。

![Encryption](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/cad96c2b06acf2b292be-6205f483-8079-4953-a5ed-3e0befff1c58.webp)

### 加密處理

基本上，在 PowerShell 之中，我們可以使用 `ConvertTo-SecureString` 來將字串轉換成**安全字串**，確保像是密碼這類的資訊可以受到很好的保護。以下 PowerShell 腳本示範如何將一個字串轉換成安全字串：

```powershell
$securepwd = ConvertTo-SecureString -String 'YourStrongPassword' -AsPlainText -Force
```

上述寫法會比較不安全，因為你在執行時，文字將會出現在 PowerShell 的歷史紀錄中！比較安全的寫法應該是：

```powershell
$securepwd = ConvertTo-SecureString -AsPlainText -Force
```

輸入的過程會要求你輸入密碼：

```
cmdlet ConvertTo-SecureString at command pipeline position 1
Supply values for the following parameters:
String:
```

你也可以用以下命令從 Console 視窗輸入一個字串並產生**安全字串**：

```powershell
$securepwd = Read-Host -AsSecureString
```

基本上你從上述命令拿到的 `$securepwd` 是無法印出來的，那是一個加密過的**安全字串**，其 .NET 的型別為 `System.Security.SecureString`！

若要將這份**安全字串**轉換成**一般字串**，可以使用 `ConvertFrom-SecureString`，如下：

```powershell
$encryptedpwd = ConvertFrom-SecureString -SecureString $securepwd
```

這裡的 `$encryptedpwd` 就是一個加密過的字串，其內容如下：

```
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000fce6da1b4890e6478a9ee3180844e27400000000020000000000106600000001000020000000e23dcbe15ad7656150ea7d4630ce2c8ac92cf78e8c15c8332e3356b54c5c4ab9000000000e80000000020000200000002b758f55072729a191d37d151f606b7c4832eb54a09ce4f398a0a94c1cddb4fa300000001a8d7a9731e4012da77cff46e33ce22f9292d3cdc3974d1d0995ee90b69343d0f72c1d62cf4f6ceffafcf56a6d985d034000000082afd2b1a6f6406030d91c952b51736d99c32e6da161459db1ebe085c3acaab3d8091fc6fb5b62929c4dd70d5efece07c91518dea632b72ec4e9e7b52571a7af
```

> 注意: 你每次執行 `ConvertTo-SecureString` 所產生的**安全字串**都會不一樣，預設他會用 [Windows Data Protection API (DPAPI)](https://en.wikipedia.org/wiki/Data_Protection_API) 來加密這個**安全字串**。

有了這段加密過的字串，你就可以將它寫入檔案中或是放在 PowerShell 腳本中，任何「人類」看到這段字串不可能快速記憶下來，就算真的「拍照」下來，在別台電腦依然無法解密，因為加密的金鑰是寫在本機電腦中的，所以有一定的安全性！

### 解密處理

你可以想像一下，我們在腳本中現在有了這一段：

```powershell
$encryptedpwd = '01000000d08c9ddf0115d1118c7a00c04fc297eb01000000fce6da1b4890e6478a9ee3180844e27400000000020000000000106600000001000020000000e23dcbe15ad7656150ea7d4630ce2c8ac92cf78e8c15c8332e3356b54c5c4ab9000000000e80000000020000200000002b758f55072729a191d37d151f606b7c4832eb54a09ce4f398a0a94c1cddb4fa300000001a8d7a9731e4012da77cff46e33ce22f9292d3cdc3974d1d0995ee90b69343d0f72c1d62cf4f6ceffafcf56a6d985d034000000082afd2b1a6f6406030d91c952b51736d99c32e6da161459db1ebe085c3acaab3d8091fc6fb5b62929c4dd70d5efece07c91518dea632b72ec4e9e7b52571a7af'
```

接下來就是在要使用密碼的時候，將他先轉回**安全字串**，如下：

```powershell
$securepwd = ConvertTo-SecureString -String $encryptedpwd
```

> 注意: 你在執行 `ConvertTo-SecureString` 時，如果跟加密時使用的電腦不同一台，那麼就無法成功轉成**安全字串**，你將會得到 `ConvertTo-SecureString : Key not valid for use in specified state.` 錯誤訊息！

如果你想要將**安全字串**轉換回**原始字串內容**，就稍微麻煩一點，步驟如下：

1.  先取得 `System.Runtime.InteropServices.Marshal` 類別實例
    
    ```powershell
    $Marshal = [System.Runtime.InteropServices.Marshal]
    ```
    
2.  使用 `Marshal::SecureStringToBSTR` 將**安全字串**轉換成 BSTR (unmanaged binary string)
    
    ```powershell
    $Bstr = $Marshal::SecureStringToBSTR($securepwd)
    ```
    
3.  使用 `Marshal::PtrToStringAuto` 將 BSTR 轉換成一般字串
    
    ```powershell
    $plaintext = $Marshal::PtrToStringAuto($Bstr)
    ```
    
4.  最後使用 `Marshal::ZeroFreeBSTR` 釋放 BSTR 的資源
    
    ```powershell
    $Marshal::ZeroFreeBSTR($Bstr)
    ```
    

另外還有一種方法可以將**安全字串**轉換回**原始字串內容**，就是使用 `PSCredential` 物件幫忙解開密碼：

1.  先透過 `New-Object System.Management.Automation.PSCredential` 建立一個 `PSCredential` 物件
    
    ```powershell
    $credential = New-Object System.Management.Automation.PSCredential('username', $securepwd)
    ```
    
2.  然後透過 `GetNetworkCredential()` 方法取得 `NetworkCredential` 物件，並透過 `Password` 屬性取得明文密碼
    
    ```powershell
    $plaintext = $credential.GetNetworkCredential().Password
    ```
    

如果你使用 PowerShell 7.3+ 的話，還可以很方便的使用 `ConvertFrom-SecureString` 搭配 `-AsPlainText` 來將**安全字串**轉換回**原始字串內容**，如下：

```powershell
ConvertFrom-SecureString -SecureString $secureString -AsPlainText
```

### 使用 PSCredential 登入服務

事實上，這個**安全字串**被使用在許多含有登入機制的服務中，你只要把**安全字串**轉成 `PSCredential` 物件，就可以使用在許多地方，例如：

```powershell
$credential = New-Object System.Management.Automation.PSCredential('username', $securepwd)
```

我以登入 [Azure AD](https://learn.microsoft.com/en-us/powershell/module/azuread/?view=azureadps-2.0&WT.mc_id=DT-MVP-4015686) 為例，只要你能拿到 `PSCredential` 物件，就可以使用 `Connect-AzureAD` 來登入 Azure AD，如下：

```powershell
Connect-AzureAD -Credential $credential
```

我再以 [Microsoft Online](https://learn.microsoft.com/en-us/microsoft-365/enterprise/connect-to-microsoft-365-powershell?view=o365-worldwide&WT.mc_id=DT-MVP-4015686) 的 PowerShell 為例，你可以使用 `Connect-MsolService` 來登入，如下：

```powershell
Connect-MsolService -Credential $credential
```

如此一來就非常方便了！

### 總結

這篇文章我介紹了如何在 PowerShell 中對機敏字串進行加解密處理，並且使用 `PSCredential` 物件來登入服務，這樣可以讓我們的腳本更加安全！

不過，你一定要清楚的知道，如果你的腳本被別人拿走的話，還是有機會取得你的「原始密碼」內容的，但前提是必須使用同一台電腦操作才行，所以還算安全。

當然，你也可以將 `$encryptedpwd` (**安全字串**的 String 型態) 儲存到另一個獨立的檔案中，或是放進 Key Vault 裡面，以增強安全性。

### 相關連結

-   [Microsoft.PowerShell.Security Module - PowerShell | Microsoft Learn](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/?view=powershell-7.3&WT.mc_id=DT-MVP-4015686)
    -   [ConvertTo-SecureString](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/convertto-securestring?view=powershell-7.3&WT.mc_id=DT-MVP-4015686)
    -   [ConvertFrom-SecureString](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/convertfrom-securestring?view=powershell-7.3&WT.mc_id=DT-MVP-4015686)
-   [Encryption and Decryption in PowerShell | by Nikhil Sureka | Medium](https://medium.com/@nikhilsda/encryption-and-decryption-in-powershell-e7a678c5cd7d)
-   [Using the PowerShell Get-Credential Cmdlet and all things credentials](https://adamtheautomator.com/powershell-get-credential/)
