mcpField
P
powershell-security

powershell-security

github.com/josiahsiegel安全59189 次安装19 周前更新
代码仓库

关于 powershell-security

介绍凭据管理、脚本加固和管理控制的 PowerShell 安全技能,面向编写脚本的人员和管理员,涵盖 SecretManagement、JEA、WDAC、签名与日志记录。

主要功能

  1. 01SecretManagement 与保管库提供程序指引
  2. 02凭据处理与 Azure 身份验证
  3. 03JEA 角色限制和审计日志
  4. 04WDAC 与受限语言控制
  5. 05脚本签名和执行策略
  6. 06输入验证与安全检查清单

适用场景

  1. 01移除脚本中的明文凭据
  2. 02限制委派的 PowerShell 管理权限
  3. 03审计特权自动化会话
  4. 04加固用于生产环境的脚本

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill powershell-security

安装后在 Claude Code 中输入/powershell-security确认已加载。

详细介绍

PowerShell 安全最佳实践(2025)

PowerShell 脚本和自动化的现代安全实践,包括凭据管理、SecretManagement 模块和强化技术。

SecretManagement 模块(2025 年推荐标准)

概述

Microsoft.PowerShell.SecretManagement 是 PowerShell 中安全存储凭据的官方解决方案。

使用 SecretManagement 的原因:

  • 切勿在脚本中存储明文凭据
  • 跨平台密钥存储
  • 支持多种保险库提供程序
  • 与 Azure Key Vault、1Password、KeePass 等集成
安装
# Install SecretManagement module
Install-Module -Name Microsoft.PowerShell.SecretManagement -Scope CurrentUser

# Install vault provider (choose one or more)
Install-Module -Name Microsoft.PowerShell.SecretStore  # Local encrypted vault
Install-Module -Name Az.KeyVault                        # Azure Key Vault
Install-Module -Name SecretManagement.KeePass          # KeePass integration
基本用法
# Register a vault
Register-SecretVault -Name LocalVault -ModuleName Microsoft.PowerShell.SecretStore

# Store a secret
$password = Read-Host -AsSecureString -Prompt "Enter password"
Set-Secret -Name "DatabasePassword" -Secret $password -Vault LocalVault

# Retrieve a secret
$dbPassword = Get-Secret -Name "DatabasePassword" -Vault LocalVault -AsPlainText
# Or as SecureString
$dbPasswordSecure = Get-Secret -Name "DatabasePassword" -Vault LocalVault

# List secrets
Get-SecretInfo

# Remove a secret
Remove-Secret -Name "DatabasePassword" -Vault LocalVault
Azure Key Vault 集成
# Install and import Az.KeyVault
Install-Module -Name Az.KeyVault -Scope CurrentUser
Import-Module Az.KeyVault

# Authenticate to Azure
Connect-AzAccount

# Register Azure Key Vault as secret vault
Register-SecretVault -Name AzureKV `
    -ModuleName Az.KeyVault `
    -VaultParameters @{
        AZKVaultName = 'MyKeyVault'
        SubscriptionId = 'your-subscription-id'
    }

# Store secret in Azure Key Vault
Set-Secret -Name "ApiKey" -Secret "your-api-key" -Vault AzureKV

# Retrieve from Azure Key Vault
$apiKey = Get-Secret -Name "ApiKey" -Vault AzureKV -AsPlainText
使用 SecretManagement 的自动化脚本
<#
.SYNOPSIS
    Secure automation script using SecretManagement

.DESCRIPTION
    Demonstrates secure credential handling without hardcoded secrets
#>

#Requires -Modules Microsoft.PowerShell.SecretManagement

[CmdletBinding()]
param()

# Retrieve credentials from vault
$dbConnectionString = Get-Secret -Name "SQLConnectionString" -AsPlainText
$apiToken = Get-Secret -Name "APIToken" -AsPlainText

# Use credentials securely
try {
    # Database operation
    $connection = New-Object System.Data.SqlClient.SqlConnection($dbConnectionString)
    $connection.Open()

    # API call with token
    $headers = @{ Authorization = "Bearer $apiToken" }
    $response = Invoke-RestMethod -Uri "https://api.example.com/data" -Headers $headers

    # Process results
    Write-Host "Operation completed successfully"
}
catch {
    Write-Error "Operation failed: $_"
}
finally {
    if ($connection) { $connection.Close() }
}

凭据管理最佳实践

切勿硬编码凭据
# ❌ WRONG - Hardcoded credentials
$password = "MyPassword123"
$username = "admin"

# ❌ WRONG - Plaintext in script
$cred = New-Object System.Management.Automation.PSCredential("admin", "password")

# ✅ CORRECT - SecretManagement
$password = Get-Secret -Name "AdminPassword" -AsPlainText
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("admin", $securePassword)

# ✅ CORRECT - Interactive prompt (for manual runs)
$cred = Get-Credential -Message "Enter admin credentials"

# ✅ CORRECT - Managed Identity (Azure automation)
Connect-AzAccount -Identity
服务主体身份验证(Azure)
# Store service principal credentials in vault
Set-Secret -Name "AzureAppId" -Secret "app-id-guid"
Set-Secret -Name "AzureAppSecret" -Secret "app-secret-value"
Set-Secret -Name "AzureTenantId" -Secret "tenant-id-guid"

# Retrieve and authenticate
$appId = Get-Secret -Name "AzureAppId" -AsPlainText
$appSecret = Get-Secret -Name "AzureAppSecret" -AsPlainText
$tenantId = Get-Secret -Name "AzureTenantId" -AsPlainText

$secureSecret = ConvertTo-SecureString $appSecret -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($appId, $secureSecret)

Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $tenantId

最小权限管理(JEA)

什么是 JEA?

最小权限管理会将 PowerShell 远程处理会话限制为特定的 cmdlet 和参数。

使用场景
  • 委派管理任务,无需授予完整管理员权限
  • 满足合规性要求(SOC 2、HIPAA、PCI-DSS)
  • 强化生产环境安全
  • 为特权操作保留审计跟踪
创建 JEA 端点
# 1. Create role capability file
New-PSRoleCapabilityFile -Path "C:\JEA\RestartServices.psrc" `
    -VisibleCmdlets @{
        Name = 'Restart-Service'
        Parameters = @{
            Name = 'Name'
            ValidateSet = 'Spooler', 'W32Time', 'WinRM'
        }
    }, 'Get-Service'

# 2. Create session configuration file
New-PSSessionConfigurationFile -Path "C:\JEA\RestartServices.pssc" `
    -SessionType RestrictedRemoteServer `
    -RoleDefinitions @{
        'DOMAIN\ServiceAdmins' = @{ RoleCapabilities = 'RestartServices' }
    } `
    -LanguageMode NoLanguage

# 3. Register JEA endpoint
Register-PSSessionConfiguration -Name RestartServices `
    -Path "C:\JEA\RestartServices.pssc" `
    -Force

# 4. Connect to JEA endpoint (as delegated user)
Enter-PSSession -ComputerName Server01 -ConfigurationName RestartServices

# User can ONLY run allowed commands
Restart-Service -Name Spooler  # ✅ Allowed
Restart-Service -Name DNS      # ❌ Denied (not in ValidateSet)
Get-Process                    # ❌ Denied (not visible)
JEA 审计日志记录
# Enable transcription and logging
New-PSSessionConfigurationFile -Path "C:\JEA\AuditedSession.pssc" `
    -SessionType RestrictedRemoteServer `
    -TranscriptDirectory "C:\JEA\Transcripts" `
    -RunAsVirtualAccount

# All JEA sessions are transcribed to C:\JEA\Transcripts
# Review audit logs
Get-ChildItem "C:\JEA\Transcripts" | Get-Content

Windows Defender 应用程序控制(WDAC)

PowerShell 脚本控制

WDAC 取代 AppLocker,用于控制哪些 PowerShell 脚本可以执行。

# Create WDAC policy for signed scripts only
New-CIPolicy -FilePath "C:\WDAC\PowerShellPolicy.xml" `
    -ScanPath "C:\Scripts" `
    -Level FilePublisher `
    -Fallback Hash `
    -UserPEs

# Allow only signed scripts
Set-RuleOption -FilePath "C:\WDAC\PowerShellPolicy.xml" `
    -Option 3 # Required WHQL

# Convert to binary policy
ConvertFrom-CIPolicy -XmlFilePath "C:\WDAC\PowerShellPolicy.xml" `
    -BinaryFilePath "C:\Windows\System32\CodeIntegrity\SIPolicy.p7b"

# Reboot to apply policy
Restart-Computer

代码签名

为什么要对脚本进行签名?
  • 验证脚本完整性
  • 满足组织安全策略
  • 启用 WDAC 强制实施
  • 防止篡改
对脚本进行签名
# Get code signing certificate
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert

# Sign script
Set-AuthenticodeSignature -FilePath "C:\Scripts\MyScript.ps1" -Certificate $cert

# Verify signature
$signature = Get-AuthenticodeSignature -FilePath "C:\Scripts\MyScript.ps1"
$signature.Status  # Should be "Valid"
执行策略
# Check current execution policy
Get-ExecutionPolicy

# Set execution policy (requires admin)
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine

# Bypass for single script (testing only)
PowerShell.exe -ExecutionPolicy Bypass -File "script.ps1"

受限语言模式

什么是受限语言模式?

限制 PowerShell 语言功能,以防止执行恶意代码。

# Check current language mode
$ExecutionContext.SessionState.LanguageMode
# Output: FullLanguage (admin) or ConstrainedLanguage (standard user)

# Set system-wide constrained language mode
# Via Environment Variable or Group Policy
# Set: __PSLockdownPolicy = 4

# Test constrained mode behavior
# FullLanguage allows:
[System.Net.WebClient]::new()  # ✅ Allowed

# ConstrainedLanguage blocks:
[System.Net.WebClient]::new()  # ❌ Blocked
Add-Type -TypeDefinition "..."  # ❌ Blocked

脚本块日志记录

启用日志记录
# Enable via Group Policy or Registry
# HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
New-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" `
    -Name "EnableScriptBlockLogging" -Value 1 -PropertyType DWord

# Log location: Windows Event Log
# Event Viewer > Applications and Services Logs > Microsoft > Windows > PowerShell > Operational
查看日志
# Query script block logs
Get-WinEvent -LogName "Microsoft-Windows-PowerShell/Operational" |
    Where-Object { $_.Id -eq 4104 } |  # Script Block Logging event
    Select-Object TimeCreated, Message |
    Out-GridView

输入验证

防止注入攻击
# ❌ WRONG - No validation
function Get-UserData {
    param($Username)
    Invoke-Sqlcmd -Query "SELECT * FROM Users WHERE Username = '$Username'"
}
# Vulnerable to SQL injection

# ✅ CORRECT - Parameterized queries
function Get-UserData {
    param(
        [ValidatePattern('^[a-zA-Z0-9_-]+$')]
        [string]$Username
    )
    Invoke-Sqlcmd -Query "SELECT * FROM Users WHERE Username = @Username" `
        -Variable @{Username=$Username}
}

# ✅ CORRECT - ValidateSet for known values
function Restart-AppService {
    param(
        [ValidateSet('Web', 'API', 'Worker')]
        [string]$ServiceName
    )
    Restart-Service -Name "App${ServiceName}Service"
}

安全检查清单

脚本开发
  • 切勿硬编码凭据(使用 SecretManagement)
  • 对 SQL 操作使用参数化查询
  • 使用 [ValidatePattern]、[ValidateSet] 等验证所有用户输入
  • 启用 Set-StrictMode -Version Latest
  • 使用 try/catch 处理错误
  • 避免将用户输入与 Invoke-Expression 一起使用
  • 对生产脚本进行签名
  • 启用脚本块日志记录
自动化
  • 使用托管身份或服务主体(切勿使用密码)
  • 将密钥存储在 SecretManagement 或 Azure Key Vault 中
  • 为委派的管理任务实施 JEA
  • 为所有特权操作启用审计日志记录
  • 遵循最小权限原则
  • 定期轮换凭据
  • 监控身份验证失败尝试
生产环境
  • 实施 WDAC 策略以控制脚本
  • 为非管理员用户使用受限语言模式
  • 启用 PowerShell 日志记录(脚本块 + 转录)
  • 要求脚本经过签名(通过执行策略)
  • 定期进行安全审计
  • 保持 PowerShell 为最新版本(7.5+)
  • 使用 JEA 进行远程管理

资源

评论

支持 Markdown