PowerShell 脚本执行受限是很多管理员在日常运维中遇到的第一个拦路虎。在 Windows Server 2012 以上的系统中,默认的执行策略通常是 Restricted,这意味着任何 .ps1 脚本文件都无法运行,只能使用交互式命令行。这不是系统有 bug,而是微软从安全角度设置的底线。要解决这个问题,最直接的办法不是盲目地改成 Unrestricted,而是根据实际运维场景分层设置执行策略。
理解执行策略的作用域和优先级PowerShell 的执行策略不是全局唯一的,它可以在多个作用域生效,而且有严格的优先级顺序。优先级从高到低依次是:MachinePolicy(组策略设定的计算机策略)、UserPolicy(组策略设定的用户策略)、Process(当前会话临时策略)、CurrentUser(当前用户注册表存储的策略)、LocalMachine(本地计算机注册表存储的策略)。很多人习惯用 Set-ExecutionPolicy 直接修改 LocalMachine,但如果域控下发了 MachinePolicy,本地修改完全无效。运维人员应该先用 Get-ExecutionPolicy -List 查看所有作用域的当前值,确认哪个层级在真正生效,再去对应层级做调整。
六种执行策略的实际差异Restricted 是 Windows 客户端的默认策略,禁止任何脚本运行,但允许交互式命令。RemoteSigned 是 Windows 服务器的默认策略,要求从互联网下载的脚本必须有受信任的发布者数字签名,本地编写的脚本可以直接运行。AllSigned 更严格,无论脚本来源,都必须有可信签名才能执行。Unrestricted 允许所有脚本运行,但从互联网下载的脚本运行前会弹出安全警告。Bypass 不做任何限制和警告,通常用于自动化部署流程中嵌入脚本的场景。Undefined 表示当前作用域未设置策略,会继承更高级别的策略。选择哪种策略,应该根据服务器的网络隔离程度、脚本来源和运维流程来决定,而不是图省事一律 Bypass。
数字签名在脚本安全中的落地实践如果企业要求使用 AllSigned 策略,所有运维脚本都必须签名。操作步骤是:先用 New-SelfSignedCertificate 创建代码签名证书,导出带私钥的 pfx 文件分发给脚本开发者,再将公钥证书导入目标服务器的受信任的发布者证书存储区。签名脚本时使用 Set-AuthenticodeSignature 命令指定证书。这里有一个容易被忽略的细节:证书有有效期,过期后已签名的脚本会直接无法运行。生产环境中建议建立证书轮换机制,并在脚本中嵌入时间戳签名,这样即使证书过期,只要签名时证书有效,脚本仍然可以运行。时间戳签名的命令需要在 Set-AuthenticodeSignature 中加上 -TimestampServer 参数,指向一个公共时间戳服务器。
用 Constrained Language 模式加固运行时安全执行策略只控制脚本能不能启动,管不了脚本启动后做什么。PowerShell 还有一层更底层的安全机制叫语言模式,通过 $ExecutionContext.SessionState.LanguageMode 可以查看当前模式。FullLanguage 拥有完整能力,ConstrainedLanguage 会禁止调用 Windows API、COM 对象、反射等危险操作。如果运维脚本只需要做文件操作、服务管理、WMI 查询这类常规任务,可以开启 Device Guard 或 WDAC 策略,将 PowerShell 锁定在 ConstrainedLanguage 模式。这样做的好处是,即使攻击者绕过了执行策略,能执行的代码能力也大打折扣。对于运行自动化任务的服务器,这是一种性价比很高的加固手段。
JEA 委派管理实现最小权限运维Just Enough Administration 是 PowerShell 内置的基于角色的访问控制方案。它的核心思路是让运维人员通过受限的 PowerShell 终结点连接到服务器,只能执行预先配置好的命令和参数。配置 JEA 需要定义角色能力文件(.psrc),指定允许哪些 cmdlet、函数、外部程序,以及参数的白名单。然后创建会话配置文件(.pssc),指定哪个安全组映射到哪个角色能力,并设置会话的语言模式、执行策略、是否允许脚本执行等。最终用户通过 Enter-PSSession 连接到这个受限终结点时,即使他本地是管理员,在会话内也只能做被允许的操作。这种方案特别适合需要给外包人员或初级运维授予部分管理权限的场景。
脚本块日志和模块日志的审计配置安全策略部署后,必须有审计手段验证效果。PowerShell 5.0 以上版本提供了脚本块日志功能,在事件查看器的 Applications and Services Logs\Microsoft\Windows\PowerShell\Operational 中,事件 ID 4104 会记录每次执行的脚本块内容,包括被混淆处理的代码也会在日志中展开。开启方法是组策略中启用“打开 PowerShell 脚本块日志记录”,并勾选“记录脚本块调用启动/停止事件”。模块日志可以记录管道执行细节,事件 ID 4103。这两项日志会产生较大数据量,建议配合 Windows 事件转发或 SIEM 系统集中收集,并设置告警规则,比如检测到包含 Invoke-Expression、DownloadString 等敏感关键词的脚本块时触发安全事件。
使用 AMSI 集成反恶意软件扫描接口Windows 反恶意软件扫描接口(AMSI)从 PowerShell 3.0 开始集成。任何脚本内容在执行前都会传递给已注册的反病毒软件进行扫描,包括从命令行直接输入的代码、通过管道传递的脚本块、以及脚本文件内容。运维人员需要确认 Windows Defender 或第三方安全软件的 AMSI 提供程序正常运行。可以通过 Get-MpComputerStatus 查看 Defender 的 AMSI 状态。值得注意的是,攻击者常用的一些混淆技术,比如字符串拼接、Base64 编码执行、反射加载,AMSI 在脚本内容被解析还原后才会扫描,所以大部分混淆手段无法绕过 AMSI。但如果安全软件本身没有及时更新签名库,AMSI 的检测效果会大打折扣,因此需要确保安全产品的自动更新机制正常运行。
PowerShell 7 与 Windows PowerShell 5.1 的安全差异很多企业还在默认使用系统自带的 Windows PowerShell 5.1,但 PowerShell 7 已经发布多年,两者在安全特性上有明显差异。PowerShell 7 基于 .NET Core,不再深度绑定 Windows 安全子系统,比如它不支持组策略控制的执行策略,只读取 LocalMachine 和 CurrentUser 的配置。它也不支持 WDAC 强制语言模式。但 PowerShell 7 引入了实验性功能,比如 PSRemoting 的 SSH 传输,可以用 SSH 密钥对认证替代传统的 Kerberos 或 NTLM,这在跨平台或多云环境中更安全。运维团队需要评估:如果服务器不需要跨平台管理,继续使用 5.1 可以利用更完整的 Windows 安全生态;如果需要管理 Linux 或使用 SSH 协议,迁移到 7 是必要的,但要额外通过其他手段补偿缺失的安全控制。
自动化管道中的凭据管理运维脚本经常需要访问数据库、调用 API、连接远程服务器,这涉及凭据存储。绝对不要在脚本中硬编码密码。PowerShell 提供了几种安全存储凭据的方案:SecretManagement 模块是微软官方推出的凭据管理框架,支持 SecretStore(本地文件加密存储)、Azure Key Vault、HashiCorp Vault 等多种后端。使用 SecretStore 时,先用 Register-SecretVault 注册保管库,再用 Set-Secret 存储凭据,脚本中通过 Get-Secret 获取。保管库本身用密码加密,交互式会话中第一次访问需要输入保管库密码,自动化场景下可以通过证书或 Windows 集成认证解锁。另一个常用方案是使用 PSCredential 对象配合 Export-Clixml,它使用 Windows 数据保护 API 加密,但只能在加密时的同一用户账户和同一台机器上解密,跨服务器使用会失败。选择哪种方案取决于自动化任务的运行账户和跨机器需求。
组策略统一管理执行策略在域环境中,应该通过组策略统一管理 PowerShell 执行策略,而不是每台服务器单独设置。组策略路径是“计算机配置\管理模板\Windows 组件\Windows PowerShell”,其中“打开脚本执行”策略可以设置执行策略,这个策略对应的是 MachinePolicy 作用域,优先级最高,本地管理员无法覆盖。同时建议启用“打开模块日志记录”、“打开脚本块日志记录”和“打开脚本执行”三组策略,形成完整的配置基线。组策略的更新周期加上 PowerShell 执行策略的读取时机,有时会导致策略下发后不立即生效,可以用 gpupdate /force 强制刷新组策略,然后重启 PowerShell 进程验证。
绕过执行策略的技术与防御思路了解攻击者如何绕过执行策略,有助于更好地防御。常见的绕过方法包括:使用 powershell.exe -ExecutionPolicy Bypass -File script.ps1 直接绕过;将脚本内容用 Base64 编码后通过 -EncodedCommand 参数传入;通过 Invoke-Expression 读取文件内容执行;使用 PowerShell 的管道输入 Get-Content script.ps1 | powershell -;甚至通过 WMI 的 Win32_Process 类远程创建 PowerShell 进程并传入脚本。防御这些手法不能单靠执行策略,需要多层控制:通过 AppLocker 或 WDAC 限制 powershell.exe 的命令行参数,禁止 -EncodedCommand 和 -ExecutionPolicy;通过约束语言模式阻止 Invoke-Expression 等危险命令;通过日志审计检测异常的进程创建模式。这些措施组合起来,才能有效降低攻击面。
