Windows PowerShell請在此簽名

Don Jones

在 2008 年 1 月份的 Windows PowerShell 專欄中,我特別強調過數位簽署 Windows PowerShell 指令碼的重要性。我也討論了一些案例來說明 AllSigned 以外的其他任何執行原則將會在環境中造成重大的安全性漏洞。

有一點我尚未詳細講解,就是如何確實簽署指令碼。您可以參閱以下網址的專欄:https://technet.microsoft.com/zh-tw/magazine/cc137728.aspx。猜猜看本月討論的主題是什麼?

不只是簽章

簽章正是我要討論的技術名詞,不過這個用語本身並不能完全闡述它在本文中的用途。與合約或信用卡帳單不同,簽署指令碼並不表示您核准或授權它。在數位安全性的世界中,簽署是一種將您的身分識別添附到某事物的處理程序,並確保「某事物」未遭到任何形式的修改。這全都是以密碼編譯為基礎,而且此密碼編譯 (至少在本例中) 會以一組加密金鑰為開頭。

這些金鑰指的是非對稱金鑰,因為這些金鑰彼此互異。這個組合包含一個只有您能存取的私密金鑰,還有一個任何人都能存取的公開金鑰。簡單來說,基本上所有使用私密金鑰加密的內容都只能以對應的公開金鑰解密。

以下是它的運作方式。同樣地,我會透過簡化的方式來討論這個過程。我有一個要簽署的 Windows PowerShell® 指令碼,還有包含要用來簽署指令碼的私密金鑰的憑證。Windows PowerShell 本身的某個命令程式可以利用指令碼和憑證進行實際的簽署,稍後我會談到。進行簽署時,Windows PowerShell 使用我的私密金鑰來加密該指令碼。加密過的複本看起來像無意義的文字,以一連串註解的形式加入指令碼底端 (見 [圖 1])。我的身分識別也編碼到這些註解中,但未加密 — 身分識別本身無須密碼編譯就可以解讀。

Figure 1 簽章區塊存放在指令碼底端

Figure 1** 簽章區塊存放在指令碼底端 **(按影像可放大)

稍後,當 Windows PowerShell 檢查簽章時,便會解碼我的身分識別並使用此身分識別來取得我的公開金鑰。由於只有我的公開金鑰可以解密簽章的其餘部分,因此 Windows PowerShell 知道假如這個公開金鑰可以解密簽章,就表示簽署的人一定是我。如果是其他人簽署的,我的公開金鑰便無法解密該簽章。一旦解密簽章之後,Windows PowerShell 會比較指令碼與加密至簽章的複本。如果兩者相符,則表示簽章完整無缺。如果兩個指令碼不同,簽章便會損毀,且視為無效。

這就是簽章如何保護純文字指令碼避免被偷改的方法。請記住,雖然指令碼本身很容易被更改,但是除非使用我的私密金鑰,否則簽章不可能變更成與修改過的指令碼相符 — 而且只有我才擁有我的私密金鑰。

我所描述的基本技術程序已經過大幅簡化。在現實生活中,簽章可以包含更多資訊,而且往往也是如此,例如公開金鑰的複本、關於發行金鑰的憑證授權單位 (CA) 的相關資訊等等。但是我在此所做的說明的確捕捉了這個程序的重要部分,也強調出簽章事實上可以保護您的指令碼避免未經授權的修改。

關鍵性的安全要素在於嚴密保護您的私密金鑰 — 您不會希望它落入他人之手。在 Windows 中安裝金鑰時,您應該立即使用密碼保護它,以免惡意處理序在您不知情的狀況下使用您的金鑰。包含您的私密金鑰的智慧卡可以提供更完善的安全性。透過使用智慧卡,您的私密金鑰永遠不會離開這張卡。相對來說,您要加密的資料會經由讀取器硬體傳輸到智慧卡上,而智慧卡本身的電路會執行加密並傳回結果。

取得憑證

本月指令程式:Export-CSV

PowerShellCommunity.org 的論壇成員常詢問 Windows PowerShell 可否匯出到 Microsoft Excel®。當然沒問題!Export-CSV 指令程式的功用就在此,因為 Excel 原本就可以開啟、編輯和儲存 CSV 檔案。舉例而言,若您要匯出服務清單,只要執行 Get-Service | Export-CSV MyServices.csv 即可。事實上,您可以將 Export-CSV 貼到任何管線尾端,那麼該管線中的所有物件都會包含在您指定的 CSV 檔案中。根據預設,Export-CSV 在檔案頂端加入標題行,以用來指定所匯出的物件類型。這一行是註解,因此不會阻礙 Excel 開啟檔案。若您不希望匯出類型資訊,只需要在 Export-CSV 中加入 -notype 參數即可。此外,您可以在不顯示提示的情況下 (預設會提示) 加入 -force 參數來強制覆寫包含相同名稱的現有 CSV 檔案,也可以加入命名巧妙的 -noClobber 參數來防止覆寫現有檔案。

若要簽署指令碼,您需要特定類型的憑證 — 第三級 Authenticode 程式碼簽署憑證 (Class III Authenticode Code-Signing Certificate) — 取得此憑證的方法有三種。第一種方法是使用貴公司內部公開金鑰基礎結構 (PKI),若有的話。實作完善的 PKI 有一個貴公司所有電腦都信任的根 CA — 若要在組織中使用 CA 發行的憑證,這是必備條件。

Windows Server® 自 Windows® 2000 起即隨附自己的憑證伺服器軟體,您可以使用此軟體來建立自己的 PKI。成熟的 PKI 實作需要詳細規劃,但是如果您使用 PKI 的唯一目的是發行程式碼簽署憑證,就不需要這麼大費周章。只要使用群組原則將 CA 根憑證發送到電腦中,這些電腦就會信任它。

第二種選擇是使用商業 CA。使用商業 CA 的優點之一是,如果您選擇其中一種主要 CA,貴公司內的電腦可能已經設為信任該 CA 的憑證 (請注意,Windows XP 在預設情況下信任大部分的商業 CA,但是 Windows Vista® 根據預設只信任一小部分)。其中一個常見的商業 CA 是 VeriSign (verisign.com)。其他還有一些值得一探究竟的商業 CA,像是 CyberTrust (cybertrust.com) 和 Thawte (thawte.com)。

取得程式碼簽署憑證的第三種作法,是使用 Makecert.exe 之類的工具建立自己的自我簽署憑證。這個工具包含在 Windows Platform SDK 內,也會安裝在某些版本的 Microsoft® Office 中,另外還有很多地方可以看到它的蹤跡。自我簽署憑證的好處除了它是免費的之外,還不需要任何基礎結構。而壞處則是它只能用於您的電腦上。但如果您只需要在您的電腦上啟用指令碼執行 — 進行程式碼簽署測試或一般實驗 — Makecert.exe 就是一個很棒的選項。有關此工具的說明文件請參閱以下網址:https://msdn2.microsoft.com/zh-tw/library/bfsktky3(VS.80).aspx。但要小心的是,這個工具的許多版本已存在多年,因此您的電腦所執行的舊版本與說明文件描述的運作方式可能會有出入。

有了 Makecert.exe 之後,您就可以建立自我簽署的憑證,方法從 Windows PowerShell 命令列是執行下列命令 (可別讓我抓到正在閱讀這篇專欄的讀者使用 cmd.exe):

makecert -n "CN=MyRoot" -a sha1 –eku
1.3.6.1.5.5.7.3.3 -r -sv root.pvk root.cer 
–ss Root -sr localMachine

接著執行下列命令:

makecert -pe -n "CN=MyCertificate" -ss MY 
–a sh1 -eku 1.3.6.1.5.5.7.3.3 -iv root.pvk 
–c root.cer

Makecert.exe 會提示您輸入密碼以保護金鑰,然後便會建立憑證。您可以在 Windows PowerShell 中執行以下程式碼來確認安裝:

gci cert:\CurrentUser\My -codesigning

這會顯示 CERT: 磁碟機 (就是您的 Windows 憑證存放區) 中屬於程式碼簽署憑證的所有項目清單。附帶一提,這些資訊也全都記錄在 Windows PowerShell 中。若要尋找該說明文件,只要執行 help About_Signing,然後參閱接下來的幾個畫面即可。

簽署指令碼

現在既然有了憑證和指令碼 (您可以視需要快速建立一個指令碼,以供測試之用),就可以開始簽署指令碼。使用 Windows PowerShell 來進行簡單的不得了 — 只要輸入:

$cert = @(gci cert:\currentuser\my
-codesigning)[0]
Set-AuthenticodeSignature myscript.ps1 $cert

第一個部分會擷取首先安裝的程式碼簽署憑證 (如果您安裝了多個憑證,而且想要使用第一個以外的憑證,只要適當變更 "0" 這個數字即可)。第二個部分會簽署檔案 (當然,您必須變更成相符的檔名)。就是這麼簡單!不過老實說,我覺得 Set-AuthenticodeSignature 指令程式名稱沒必要這麼長,因此我建立了稱為 Sign 的別名。現在,我只要如下執行:

Sign myscript.ps1 $cert

開啟這段指令碼,您會看到簽章區塊被插到底部。試著使用設為 AllSigned (Set-ExecutionPolicy AllSigned) 的執行原則來執行此指令碼,應該能正常運作。現在您要修改並儲存指令碼,不過請確認您沒有再次簽署它。Windows PowerShell 現在應該會直接拒絕執行修改過的版本,因為簽章已損毀。

一切都值得

這些程序步驟這麼麻煩,真的值得嗎?想要編寫一些系統管理工作的指令碼,又不想花 500 美元取得憑證或實作全新的基礎架構項目,這樣要求會太過份嗎?坦白說,我自己也是系統管理員,您的顧慮我也懂。但是答案很簡單,您所花的一切努力絕對都值得。

事實就是,維護安全性總是會牽涉或多或少的麻煩事。防毒軟體可能會造成您的不便,可是您不可能不使用它。防火牆軟體更是討厭,但您也知道,不使用防火牆執行系統就不安全。Windows Vista 使用者帳戶控制 (UAC) 可能也很麻煩,但是它能讓您的電腦更安全。而我們努力想達到的,不正是這樣強化的安全性嗎?

Windows PowerShell 是一個功能強大的工具,就像其他工具一樣,一定有可能被惡意使用者扭曲而變成安全性弱點。因此您必須採取各種步驟來遏止這些惡意使用者。

程式碼簽署是您所能採取的最佳行動,而且不必花費太多精力。譬如說,我使用圖形化指令碼編輯器,在我每次按下 [儲存] 時自動簽署我的指令碼,因此程式碼簽署對我來說幾乎沒什麼負擔。

即使沒有花俏的編輯器,您也可以不費吹灰之力。舉例來說,您可以建立自己的 Windows PowerShell 設定檔 (在您的文件資料夾中名為 WindowsPowerShell 的資料夾下建立檔案 Microsoft.PowerShell_profile.ps1),然後加入以下函式:

function sign ($filename) {
  $cert = @(gci cert:\currentuser\my 
-codesigning)[0]
Set-AuthenticodeSignature $filename $cert
}

現在您只要輸入下列內容就可以簽署指令碼:

Sign c:\scripts\myscript.ps1

當然,您要簽署的第一個指令碼應該就是設定檔指令碼。重點就是花幾個步驟,讓指令碼簽署盡可能方便使用,而不造成您的困擾。

專為發行程式碼簽署憑證而部署單一伺服器 PKI 並不需要費什麼功夫 (但是請記得,您確實需要調查並規劃您的 PKI,包括保護根 CA 和提供嚴重損壞修復,尤其是它還有其他用途的時候)。如果您是唯一一個需要在環境中執行指令碼的人,始終可以使用 Makecert.exe。請注意,About_Signing Windows PowerShell 說明主題也包括如何保障使用 Makecert.exe 所產生的憑證安全性的相關資訊。

Don Jones 是《Windows PowerShell: TFM and VBScript, WMI, and ADSI Unleashed》的作者。您可以透過 PowerShellCommunity.org 網站與他聯繫。

© 2008 Microsoft Corporation and CMP Media, LLC. 保留所有權利;未經允許,嚴禁部分或全部複製.