Windows PowerShell请在此处签名

Don Jones

在 2008 年 1 月的 Windows PowerShell 专栏中,我曾强调过为 Windows PowerShell 脚本进行数字签名的重要性。当时我还列举了一些假设情况,以说明任何非 AllSigned 执行策略都可能使您的环境出现重大安全漏洞。

但有一件事我没有详细做介绍,即如何在实际中为脚本签名。您可登录 technetmagazine.com/issues/2008/01/PowerShell 浏览该专栏。猜猜这个月我要讲些什么?

不仅仅是签名

签名一词相对于我要论述的内容而言是一个恰当的技术术语,尽管这个单词本身并没有真正透彻说明将要发生的情况。这里所说的签名脚本并不意味着您要批准或授权它,就像在合同或信用卡上签名那样。在数字化的安全世界里,签名是指一种过程,它将您的身份附加到某个对象上,并确保该“对象”的状态或条件未经过任何方式的修改。它完全以密码学为基础,密码学(至少在这种情况下)是以一对加密密钥开始的。

这些密钥指非对称密钥,因为它们彼此间互不相同。密钥对包括一个私钥(只能由您使用)和一个公钥(任何人都可以使用)。我将其做了一些简化,但基本来说,任何使用私钥加密的内容只能使用匹配的公钥来解密。

下面介绍它的工作原理。同样,在这里我还将使用一些简化的方法以方便讨论。我有一个想要进行签名的 Windows PowerShell® 脚本,同时还有一个证书,其中包含要用于对脚本进行签名的私钥。Windows PowerShell 本身有一个 cmdlet(我将在稍后对其进行讨论),它可以获取脚本和证书并执行实际的签名。当它执行此操作时,Windows PowerShell 会获取脚本并使用我的私钥来加密它。加密的副本显示为一些杂乱的文字,被添加到脚本底部作为一系列注释(请参见图 1)。我的身份信息也被编码(但未加密)加入到这些注释中—身份信息本身不必借助密码学即可读取。

Figure 1 位于脚本底部的签名区块

Figure 1** 位于脚本底部的签名区块 **(单击该图像获得较大视图)

稍后,当 Windows PowerShell 查看签名时,它将解码我的身份并使用它来获取我的公钥。由于只有我的公钥可以解密签名的其余内容,因此 Windows PowerShell 知道,如果它能够解密签名,则我肯定已经对其进行过签名。如果其他人也对其进行过签名,则我的公钥将无法解密此签名。签名被解密后,Windows PowerShell 会将脚本与被加密到签名中的副本进行比较。如果它们匹配,则认为签名是未经改动的。如果两个脚本不同,则说明签名被破坏,它将被认为是无效的。

签名就是通过这种方式来防止明文脚本被他人私自篡改的。请记住,尽管脚本自身很容易地就可以被改动,但是如果不使用我的私钥就无法更改签名来匹配更改过的脚本—但只有我有自己的私钥。

这就是最基本的技术流程简要介绍。事实上,签名能够并且的确包含更多的信息,例如我的公钥副本、有关发放该密钥的证书颁发机构 (CA) 的信息等等。但是我在本文中的描述显然指出了该流程的重要部分,并且强调了这一事实:即签名实际上可以防止您的脚本受到未经授权的修改。

关键的安全防范要素是切实保护好您的私钥—绝不要使其落入他人之手。在 Windows 中安装您的密钥时,应立即将其使用密码保护起来,以防恶意进程在您不知情的情况下使用您的密钥。智能卡可以提供更好的安全性,因为它们包含您的私钥。使用智能卡,您的私钥绝不会脱离开该卡片。您想要加密的数据通过其读卡器硬件传送到卡中,卡片自身的电路将执行加密过程并发回结果。

获得证书

本月 Cmdlet:Export-CSV

PowerShellCommunity.org 论坛的人员经常询问 Windows PowerShell 是否能导出到 Microsoft Excel®。当然能!Export-CSV cmdlet 可实现这一目的,因为 Excel 本来就可以打开、编辑和保存 CSV 文件。例如,如果您希望导出一个服务列表,只需运行 Get-Service | Export-CSV MyServices.csv 即可。实际上,您可以将 Export-CSV 附加到任何管道的末尾,该管道中的所有对象都将在您指定的 CSV 文件中终止。在默认情况下,Export-CSV 将标题行添加到文件的顶部,用来指定导出对象的类型。这一行会被注释掉,因此不会对 Excel 打开该文件产生影响。如果不希望导出该类型的信息,只需向 Export-CSV 添加 -notype 参数即可。此外,您可以添加 -force 参数,以便在不提示的情况下(默认是提示)强制覆盖同名的现有 CSV 文件,也可以添加相应命名的 -noClobber 参数以防止覆盖现有文件。

要签名脚本,您需要有一个特定类型的证书,即 Class III Authenticode 代码签名证书,获取该证书的途径主要有三种。第一种是使用组织的内部公钥基础结构或 PKI(如果有的话)。一个正确实施的 PKI 会使其根 CA 在组织的所有计算机中都是受信任的,这对于确保该 CA 所发放的证书在组织中的可用性是必需的。

Windows Server® 自 Windows® 2000 后就开始附带自己的证书服务器,因此您可以使用该软件创建自己的 PKI。一个完善的 PKI 实施需要做大量的规划,但是如果只需要一个以发放代码签名证书为唯一目的的 PKI,则不需要太多的工作。只需使用组策略将 CA 的根证书推送到您的计算机并使其信任该证书即可。

第二个选择是使用商业 CA。使用商业 CA 的一个优势在于,如果您选择其中一个主要 CA,则组织中的计算机可能已经配置为信任来自该 CA 的证书。(注意,Windows XP 在默认情况下信任大量证书,而 Windows Vista® 在默认情况下信任的证书要少得多)。一个常用的商业 CA 是 VeriSign (verisign.com)。还有其他一些也值得考虑,例如 CyberTrust (cybertrust.com) 和 Thawte (thawte.com)。

获得代码签名证书的第三种选择是使用 Makecert.exe 等工具来制作自己的自签名证书。它与 Windows Platform SDK 一起提供,安装在某些版本的 Microsoft® Office 中,在许多其他位置也可以找到它。自签名证书的优势在于它是免费的,并且不需要任何基础结构。但劣势也很明显,它只能在您自己的计算机上使用。但是如果只是出于对代码签名进行测试或进行一般性实验目的在计算机中启用脚本执行,则 Makecert.exe 不失为一种不错的选择。go.microsoft.com/fwlink/?LinkId=108538 提供了有关此工具的文档。请注意,此工具的许多版本在多年前即已存在,因此可能您的计算机运行的是较早的版本,其工作方式与文档说明会有些差异。

有了 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: drive(Windows 证书存储位置)中显示所有代码签名证书项目的列表。此外,所有这一切还将被记录在 Windows PowerShell 自身中。要查找该文档,只需运行 help About_Signing 然后向下翻阅几个屏幕即可。

签名脚本

现在您已获得了证书和脚本(出于测试目的,您可以只快速获取一个,如果需要的话),接下来就可以签名脚本了。使用 Windows PowerShell 非常容易实现,只需输入:

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

第一部分将检索首次安装的代码签名证书(如果安装了多个证书并希望使用第一个以外的其他证书,只需将 "0" 改为相应的数字即可)。第二部分签名文件(当然,您需要更改文件名以匹配自己的文件)。就这么简单!但是说实话,我发现 Set-AuthenticodeSignature cmdlet 这个名称显得稍微长了一些,这就是我创建一个名为 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 与 CMP Media, LLC.保留所有权利;不得对全文或部分内容进行复制.