在Windows Server环境中,提升安全性最直接有效的手段之一,就是通过组策略强制PowerShell只能执行经过数字签名的脚本。这能从根本上阻断未授权或恶意脚本的运行,因为任何没有有效签名的.ps1文件都会被直接拒绝执行。要实现这一目标,核心在于配置PowerShell的执行策略(Execution Policy)。很多人习惯在PowerShell窗口里用Set-ExecutionPolicy命令,但这只是当前用户的局部设置,很容易被绕过。真正牢靠的做法,是通过组策略在计算机层面锁定该配置,让所有登录这台服务器的用户都受到同等约束。

理解执行策略的作用域与优先级

在动手之前,需要先理清Windows PowerShell执行策略的几个层级。它并非只有一个开关,而是存在多个作用域,优先级从高到低依次为:MachinePolicy(组策略计算机配置)、UserPolicy(组策略用户配置)、Process(当前会话)、CurrentUser(当前用户注册表)、LocalMachine(本地计算机注册表)。当你在PowerShell里运行Set-ExecutionPolicy Restricted时,修改的是LocalMachine或CurrentUser作用域。但如果域控下发了组策略中的MachinePolicy,那么无论本地怎么改,都会被组策略覆盖。因此,要真正实现“仅执行签名脚本”,必须在组策略里设置MachinePolicy为AllSigned或RemoteSigned。其中AllSigned最为严格,要求所有脚本包括本地编写的都必须签名;RemoteSigned则相对宽松,要求从网络下载的脚本必须签名,本地脚本可不受限。对于服务器环境,强烈建议使用AllSigned。

通过组策略管理模板配置执行策略

Windows Server默认并没有在组策略管理控制台里直接显示PowerShell执行策略的选项,需要先获取并部署相关的管理模板文件。这些文件通常以.admx和.adml格式存在。如果你的域控制器或管理工作站运行的是Windows Server 2016及以上版本,系统已内置了这些模板。具体路径为:在组策略管理编辑器里,定位到“计算机配置” -> “策略” -> “管理模板” -> “Windows组件” -> “Windows PowerShell”。在这里,你会看到一个名为“打开脚本执行”的策略设置。双击打开它,将其设置为“已启用”,然后在下方的“执行策略”下拉菜单中,根据需求选择“仅允许已签名的脚本”即对应AllSigned,或“仅允许本地脚本和远程已签名脚本”对应RemoteSigned。点击确定后,这个策略就会应用到所链接的组织单位内所有服务器。

没有管理模板时的补救方案

如果你发现组策略路径下根本找不到Windows PowerShell这个节点,说明当前域控的中央存储库缺少相关模板。此时无需慌张,可以从任何一台安装了PowerShell的Windows Server上复制模板文件。源文件通常位于C:\Windows\PolicyDefinitions\,找到PowerShellExecutionPolicy.admx和对应语言目录下的PowerShellExecutionPolicy.adml。将.admx文件复制到域控的\\<域名>\SYSVOL\<域名>\Policies\PolicyDefinitions\下,将.adml文件复制到该路径下的对应语言子文件夹中。等待Active Directory复制生效后,再次打开组策略管理编辑器,就能看到相关选项了。这个步骤是很多管理员在实际操作中容易卡壳的地方,因为界面里找不到选项就以为无法配置,其实只是模板缺失。

使用组策略首选项注册表方式强制写入

如果因为某些原因无法使用管理模板,或者希望更灵活地控制,可以直接通过组策略首选项来修改注册表。PowerShell的执行策略在计算机层面存储在注册表路径HKLM\Software\Policies\Microsoft\Windows\PowerShell中。具体操作是:在组策略管理编辑器里,定位到“计算机配置” -> “首选项” -> “Windows设置” -> “注册表”。右键新建一个注册表项,操作类型选择“更新”,配置单元为HKEY_LOCAL_MACHINE,键路径为Software\Policies\Microsoft\Windows\PowerShell。然后在该项下新建一个字符串值,数值名称为ExecutionPolicy,数值数据设置为AllSigned。再新建一个DWORD值,数值名称为EnableScripts,数值数据设置为1。这样,组策略刷新后,PowerShell的执行环境就会被强制锁定。这种方式不依赖.admx模板,在任何Windows Server版本上都通用,而且执行效率很高。

验证策略是否生效与排查故障

策略下发后,在目标服务器上以管理员身份打开PowerShell,运行命令Get-ExecutionPolicy -List,会看到MachinePolicy作用域已经显示为AllSigned。此时如果尝试运行一个未签名的测试脚本,系统会抛出错误,提示“无法加载文件,因为在此系统上禁止运行脚本”。这表明策略已生效。但实际运维中,经常会遇到策略刷新延迟的问题。可以手动运行gpupdate /force强制刷新组策略,然后重启PowerShell会话再查看。如果依然不生效,需要检查组策略继承是否被阻断,或者服务器是否在正确的组织单位内。另外,PowerShell 7与Windows PowerShell 5.1的策略存储位置不同,上述方法仅针对Windows PowerShell内置于系统的版本。如果环境里安装了PowerShell 7,需要单独为其配置执行策略,因为它使用独立的配置文件路径,不受组策略中Windows PowerShell节点控制。

代码签名证书的准备与脚本签名操作

强制仅执行签名脚本后,所有要在服务器上运行的.ps1文件都必须经过数字签名。这需要一张代码签名证书。在内部环境中,可以搭建企业CA(证书颁发机构)来签发代码签名证书,成本较低且管理方便。从证书颁发机构获取证书后,将其导入到当前用户的个人证书存储区。然后使用Set-AuthenticodeSignature命令对脚本进行签名。示例操作如下:

# 获取证书,假设证书名称为"Internal Code Signing"
$cert = Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert | Where-Object {$_.Subject -like "*Internal Code Signing*"}

# 对脚本进行签名
Set-AuthenticodeSignature -FilePath C:\Scripts\MyScript.ps1 -Certificate $cert

签名完成后,脚本文件末尾会追加一个签名块。之后运行该脚本时,PowerShell会验证证书链是否受信任。如果证书是由内部CA签发,必须确保所有执行该脚本的服务器都将该CA的根证书安装到了“受信任的根证书颁发机构”存储区。否则即使脚本已签名,也会因为证书链不受信任而被拒绝执行。这是很多管理员容易忽略的环节,导致策略配置正确但脚本依然无法运行。

处理模块和脚本点源的特殊情况

强制AllSigned后,不仅直接运行的脚本受限,通过点源引入的函数或模块也会受到同样约束。如果脚本A使用点操作符加载了未签名的脚本B,整个执行过程会中断。这意味着所有被调用的.ps1文件,包括那些被用作函数库的脚本,都必须签名。对于模块文件.psm1,情况类似。如果模块清单.psd1中通过ScriptsToProcess或RequiredModules引用了未签名文件,导入模块时也会失败。因此,在实施该策略前,需要全面梳理服务器上所有PowerShell脚本资产,统一进行签名处理。可以编写一个批量签名脚本,遍历指定目录下的所有.ps1和.psm1文件,逐一调用Set-AuthenticodeSignature完成签名,确保不遗漏。

绕过策略的潜在风险与防御

即便配置了AllSigned,仍存在一些技术手段可能绕过执行策略。例如,攻击者可以将脚本内容直接粘贴到PowerShell交互式控制台中逐行执行,这不属于“运行脚本文件”,因此不受执行策略限制。此外,使用PowerShell -Command参数传递脚本代码,或者将脚本内容编码为Base64字符串通过-EncodedCommand参数执行,也能绕过文件层面的签名检查。更隐蔽的是,通过.NET反射加载包含恶意代码的程序集,完全脱离PowerShell脚本执行上下文。针对这些绕过手法,除了配置执行策略外,还需要配合其他安全措施:启用PowerShell脚本块日志记录,通过组策略开启模块日志和转录功能;配置AppLocker或Windows Defender应用程序控制,限制PowerShell只能执行特定路径下的脚本;使用约束语言模式,限制PowerShell语言中敏感元素的访问。多层防御才能构建真正可靠的安全屏障。

结合Just Enough Administration实现精细控制

对于运维自动化需求较强的环境,仅靠AllSigned可能过于粗放。Windows Server提供了JEA(Just Enough Administration)功能,允许创建受限的PowerShell会话端点。通过JEA,可以指定哪些用户或组能够执行哪些命令,甚至限制参数取值范围。JEA端点配置文件中可以嵌入执行策略设置,与组策略下发的MachinePolicy形成互补。例如,域控通过组策略锁定全局为AllSigned,同时为特定运维团队配置JEA端点,允许他们在受控环境中运行经过审核的命令。这样既保证了基线安全,又保留了必要的运维灵活性。JEA的会话记录功能还能将所有操作转录为文本,便于事后审计。

监控与持续合规

策略部署不是终点,持续监控才是保障安全的关键。应该配置安全信息和事件管理系统来收集PowerShell相关日志。重点关注事件ID 4104(脚本块日志)、4103(模块日志)和800(管道执行详情)。当检测到脚本执行被签名策略阻止时,事件日志中会记录相应错误。可以设置告警规则,当短时间内出现大量执行策略阻止事件时,可能意味着有人正在尝试运行未授权脚本,需要立即排查。另外,定期检查证书有效期也很重要,代码签名证书通常有1到3年的有效期,过期后所有使用该证书签名的脚本都会失效,导致业务中断。建议在证书到期前至少一个月启动续签和重新签名流程。

通过组策略强制PowerShell仅执行签名脚本,是Windows Server安全加固中性价比极高的措施。它从操作系统层面切断了恶意脚本运行的主要通道,且配置后几乎不需要日常维护。关键在于理解执行策略的作用域机制,正确部署管理模板或注册表项,并建立配套的代码签名基础设施。当这些环节都落实到位后,服务器的PowerShell攻击面将大幅缩减,即使攻击者获得了低权限账户,也无法通过脚本实施横向移动或持久化驻留。