Windows PowerShell保护 Shell

Don Jones

当 Windows PowerShell 团队着手创建一个新的外壳,但工作过程中不断提到“脚本”时,在整个 Redmond 周围可能会听到一片失望的声音。毕竟,Microsoft 先前在管理脚本(此处指 VBScript)方面投入的大量工作不能说完全没有问题。一个

过度授权的执行模型以及大多数用户希望作为完全权限管理员登录的意愿,为灾难敞开了大门。

参加第一次 Windows PowerShell™ 会议的人员一定祈祷过:“想必我们此后不会再遇到此类情形了。”他们确实不会再遇到了。Microsoft 的安全性声誉有了极大提高,这在很大程度上是由于 Microsoft 首先考虑的是安全性问题,而不是随后在产品开发周期中才关注此问题。此原理在 Windows PowerShell 中得到了充分体现。

为什么我的脚本不能运行?

将 Windows PowerShell 安装在一台新的计算机上,然后双击一个 .ps1 文件:此时会弹出记事本,而不是 Windows PowerShell。这是由于 .ps1 文件扩展名(用于 Windows PowerShell 脚本的扩展名)与外壳本身没有任何关联。换句话说,您无法仅通过双击来运行脚本。运行脚本的唯一方法是:打开 Windows PowerShell,键入脚本名称,然后按 Enter。

实际上,仅键入脚本名称也是不够的。您可以从图 1 中看到文件 Demo1.ps1 位于当前文件夹中,但键入 demo1 然后按 Enter 会出现一个错误:“无法将‘demo1’一词识别为 cmdlet、函数、可操作程序或脚本文件。”这是为什么?毕竟,文件确实就在那里。

图 1 为避免命令攻击,Windows PowerShell 要求指定脚本的路径

图 1** 为避免命令攻击,Windows PowerShell 要求指定脚本的路径 **(单击该图像获得较大视图)

这是 Windows PowerShell 安全模型的另一方面。一个欺骗性恶意用户在其他外壳中所做的尝试通常是使用与内置命令相同的名称创建一个脚本。因此,例如,如果您希望某个用户运行您的脚本,那么您可以将该脚本命名为 Dir.ps1,并将其放到一个文件夹中。如果您确信该用户会键入 Dir 并按 Enter,那么您的脚本就可以运行,而不是运行用户预期的 Dir 命令。这一技术称为命令攻击。在 Windows PowerShell 中,您必须始终提供此类脚本的路径,以确保 Windows PowerShell 能够很好地防止命令攻击。

运行 demo1 无效,因为没有指定路径,但运行 ./demo1 却有效。这是因为现在我已指定了一个路径,即当前文件夹。该命令行与内置命令混淆的可能性很小,因为如果要引用内置命令,从来不需要键入任何类型的路径。因此,要求键入路径有助于 Windows PowerShell 避免在按 Enter 后可能发生的命令攻击和混淆。

脚本执行策略

您已获得全新安装的 Windows PowerShell 副本,您正在使用正确的语法尝试运行某个脚本。令您吃惊的是,您遇了另一条错误消息,通知您不允许 Windows PowerShell 运行脚本。不明白???下面将介绍外壳的执行策略。

您可以通过在外壳中运行 Get-ExecutionPolicy 来查看当前的执行策略是什么。默认情况下,设置为“Restricted”。很简单,这个策略意味着脚本将无法运行。任何时候,对于任何人均是如此。默认情况下,Windows PowerShell 只能以交互方式使用,而不能用于运行脚本。您可以使用 Set-ExecutionPolicy cmdlet 从以下四种可能的执行策略设置中选择一种:

  • Restricted,默认设置,不允许运行任何脚本。
  • AllSigned,仅运行受信任脚本(稍后会详细介绍)。
  • RemoteSigned,运行本地脚本,不管这些脚本是否受信任;如果是从 Internet 下载的脚本,则必须是受信任的脚本才能够运行。
  • Unrestricted,允许运行所有脚本,甚至是不受信任的脚本。

坦白说,对于任何生产计算机而言,都应该最低设置为 AllSigned。我发现,RemoteSigned 对于开发和测试环境很有帮助,但对普通用户而言却没有必要。我还发现,Unrestricted 无论在哪种情形下都是没有用处的,所以即使将来的某些 Windows PowerShell 版本忽略了这一过度授权的设置也不必介意。

信任问题

您的计算机已预配置为信任某些根证书颁发机构 (CA),即颁发数字证书的服务器。图 2 显示了“Internet 选项”控制面板应用程序,该程序列出了受信任的根 CA。在 Windows Vista® 中,此列表很短,仅包含一些主要的商业根 CA。相反,Windows® XP 中的此列表很长,包含很多我从未听过的根 CA。如果某个根 CA 位于此列表中,基本上就表示您信任该公司,它会对根 CA 进行操作,使其负责在向用户颁发数字证书前验证用户身份。

您获得第 III 类数字证书(通常称为代码签名证书)时,CA(可以是商业 CA,也可以是位于贵组织中的专用 CA)必须验证您的身份。此类数字证书类似于电子通行证或身份证。例如,在提供“我是 Don Jones”ID 之前,CA 需要执行一些步骤以确保我确实是 Don Jones。当您使用自己的证书对 Windows PowerShell 脚本进行数字签名时(可以使用 Set-AuthenticodeSignature cmdlet 执行此操作),表示您正在将您的名字签署到脚本上。当然,如果您能够获得包含其他人姓名的错误证书,就可以将其姓名签署到脚本上。因此,图 2 中的列表上仅显示受信任的 CA 是很重要的。

图 2 Windows Vista 中默认受信任的根 CA

图 2** Windows Vista 中默认受信任的根 CA **(单击该图像获得较大视图)

当 Windows PowerShell 检查脚本是否受信任时(在 AllSigned 和 RemoteSigned 执行策略设置下执行这一过程),会提出三个问题:

  • 此脚本是否已签名?如果没有签名,则该脚本就是不受信任的。
  • 签名是否完整?即,脚本在签名后是否进行过更改?如果签名不完整,则该脚本就是不受信任的。
  • 签名是否由使用受信任的根 CA 颁发的数字证书创建?如果不是,则该脚本就是不受信任的。

本月 cmdlet:Set-AuthenticodeSignature

Set-AuthenticodeSignature 是对您的 Windows PowerShell 脚本进行数字签名的关键,这样可以使用外壳的最安全的执行策略 AllSigned。若要使用此 cmdlet,首先需要运行另一个 cmdlet — Get-ChildItem,后者用于获得已安装的代码签名证书:

$cert = Get-ChildItem –Path cert:\CurrentUser\my –codeSigningCert

您需要使用指向某个已安装证书的路径来替换该文件路径,可以通过 cert:drive 获得所有已安装的证书。加载证书后,运行以下命令可以对脚本进行签名:

Set-AuthenticodeSignature MyScript.ps1

–cert $cert

当然,要提供您的 .ps1 脚本文件的完整路径。该外壳将向该文件添加回签名块。

信任如何提高安全性?当然,黑客可能会编写恶意脚本、签署该脚本,并获取您环境中某位用户的信任从而执行该脚本。由于该脚本已签名,因此将运行。但正是由于该脚本已签名,您就可以使用该签名来查找作者的身份,继而采取相应措施(如联系执法机构)。但是,只有非常愚蠢的人才会在创建恶意脚本后署上自己的真实姓名!

当然,如果恶意用户能够获取其他用户的身份证书(例如,从身份检查过程不完备的 CA 获取),您将无法根据签名识别真正的罪犯。这正是您信任的根 CA 应具有令您满意的身份验证过程的原因。

如果您使用的是 AllSigned 执行策略设置,甚至还必须对您生成的每个脚本进行数字签名,然后才能运行。Set-AuthenticodeSignature cmdlet 使用起来相当简单,但进行这种数字签名的过程仍很麻烦。此时,第三方软件可能派上用场。建议您选择一个可以使用您指定的证书自动签署脚本的脚本编辑器或可视开发环境。这样,使用签名就是一个透明的过程,并且不必花费多余的精力。如果您希望获得有关支持此方法的编辑器和开发环境的建议,请访问一些在线脚本论坛(如我的网站 ScriptingAnswers.com),并发表消息询问其他 Windows PowerShell 爱好者使用的是什么。

获得证书后,您还可以运行以下一行式命令签署特定位置中的所有脚本:

Get-Childitem *.ps1 | %{Set-AuthenticodeSignature $_.fullname $cert}

集中式保护

显然,Windows PowerShell 执行策略可以对计算机逐一进行配置。但对于企业环境而言,这并不是一个好的解决办法。您可以使用组策略来代替该方法。(您可以从 go.microsoft.com/fwlink/?LinkId=93675 下载 Windows PowerShell 管理模板。)只需将文件拖动到能够影响您的整个域的组策略对象 (GPO) 中,并将其设置为 Restricted 即可。这样,无论将 Windows PowerShell 安装在何处,您都能够保证脚本不会运行。继而,您可以只对本应运行脚本的那些计算机(如您自己的工作站)应用限制性更弱的设置(如 AllSigned)。

备用凭据

不同于 Windows 以前的任何脚本管理语言,Windows PowerShell 甚至已准备好以安全的方式处理备用凭据。例如,Get-WMIObject cmdlet 具有一个 –credential 参数,该参数允许您为远程 WMI 连接指定备用凭据。该参数将接受用户名但不接受密码,由此可防止将敏感的密码硬编码到某个明文脚本。在您提供用户名时,Windows PowerShell 会使用一个对话框提示您输入密码。如果您计划再次使用某个备用凭据,则可以使用 Get-Credential cmdlet 存储身份验证信息:

$cred = Get-Credential DOMAIN\USER

这会立即提示您输入密码,并会将最终凭据存储在变量 $cred 中。随后,变量 $cred 将会根据需要随时被传送给 –credential 参数。您甚至可以使用 ConvertTo-SecureString cmdlet 和 ConvertFrom-SecureString cmdlet 将凭据转换为加密的字符串,或将先前加密的字符串转换回凭据。

这样做会出现一个问题,即您的加密密钥最终会存储到明文脚本,这就完全破坏了安全性。因此,您可以通过向您的 Windows PowerShell 配置文件添加一个对 Get-Credential 的调用,以此替换该操作。然后,当 Windows PowerShell 运行时,会立即提示您输入用户名和密码,该信息将存储在名为 $cred 的变量中。这样,您的外壳中会一直存在一个可用的 $cred 变量,用来表示域管理员帐户。并且,您再也不用为将该凭据存储在何处而烦恼了,因为在您每次打开外壳时都会重新创建凭据,没有必要进行存储。

默认安全

默认情况下,Windows PowerShell 将以支持脚本的管理外壳所能够提供的最安全的设置进行安装和配置。除非您更改了其中的任意一项设置,否则 Windows PowerShell 将一直处于它所能达到的最安全状态。换句话说,Windows PowerShell 中降低安全性的任何操作都是由您的决定和操作导致的。因此,在更改任何默认设置(如重新配置文件扩展名关联、更改执行策略等)前,请确保已完全了解您的操作将带来的后果,并准备好为您所做的更改负责。

Don Jones是 SAPIEN Technologies 的首席脚本权威,也是 ScriptingTraining.com 的一名讲师。您可以通过网站 scriptingAnswers.com 与 Don 联系。

© 2008 Microsoft Corporation 与 CMP Media, LLC.保留所有权利;不得对全文或部分内容进行复制.