PowerShellのセキュリティ強化 ── ログ・AMSI・言語モード・JEA

· · PowerShell, セキュリティ, Windows, ログ, 監査, 情報システム, 権限管理, 運用改善

更新履歴(初版のみ・2026年07月25日公開)
初版公開
この記事を引用する(DOI: 10.5281/zenodo.21547462)

この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。

小村 豪(2026)「PowerShellのセキュリティ強化 ── ログ・AMSI・言語モード・JEA」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21547462

DOI(最新版)
10.5281/zenodo.21547462
DOI(この版)
10.5281/zenodo.21547463

「侵入されたときにPowerShellが悪用された」という報告が続いた結果、社内でPowerShellの利用を全面禁止しようとする組織があります。しかしこれは、実効性の面でも業務影響の面でも割に合いません。PowerShellはWindowsの管理基盤そのものであり、止めれば運用の自動化が止まります。一方で攻撃側は、同等のことを別の手段で実行できます。

現実的な方針は、禁止ではなく可視化と制限です。幸い、PowerShell 5.0以降には防御側のための機能が揃っています。難読化されたコードでも展開後の形で記録するスクリプトブロックログ、実行前にウイルス対策製品へ内容を渡すAMSI、実行できる構文自体を制限する言語モード、そして「必要な操作だけを委任する」JEA。これらを組み合わせれば、業務を止めずに監査可能な状態を作れます。

この記事では、社内のWindows環境でPowerShellを安全に使い続けるために設定すべき項目を、効果の高い順にまとめます。実行ポリシーと署名については「PowerShellの実行ポリシーとスクリプト署名」で扱っているので、本記事はその先を扱います。

1. まず結論

  • 最優先はスクリプトブロックログの有効化です。実行されたコードがイベントログに記録され、難読化されたコードも展開後の形で残ります(イベントID 4104)。1
  • トランスクリプションも併せて有効にします。入出力を含むセッションの記録を、書き込み専用の共有に集約できます。1
  • ログを有効にしたら、ログのサイズ設定も見直します。既定のままだと短期間で上書きされます。1
  • AMSIにより、実行前のスクリプトがウイルス対策製品に渡されます。PowerShell 5.0以降、Windows 10以降で有効です。2
  • 古いPowerShell 2.0エンジンは無効化します。残っていると、ログもAMSIも効かない古いエンジンへ切り替えられる余地が残ります。3
  • 実行ポリシーはセキュリティ境界ではありません。公式が明記しています。防御の主軸にしないでください。4
  • 言語モードはWDAC/AppLockerの結果として使います。手動設定は回避可能で、セキュリティ機能として機能しません。5
  • 権限委任にはJEA。「このコマンドの、このパラメーターだけ」を許可し、管理者権限を配らずに運用を回せます。6

2. 何を守るのか ── 可視化が先、制限が後

対策を並べる前に、優先順位を決めます。

段階 やること 効果
1. 可視化 スクリプトブロックログ、トランスクリプション 何が起きたか分かる。事後調査が可能になる
2. 基礎的な無害化 PowerShell 2.0の無効化、最新版の維持、AMSIの確認 検査を迂回する経路を塞ぐ
3. 権限の限定 JEAによる委任、管理者権限の削減 被害の範囲を限定する
4. 実行の制限 WDAC/AppLocker + 制約付き言語モード 未承認コードの実行自体を止める

多くの現場では、1と3をやるだけで状況が大きく改善します。4は導入コストが高いため、業務影響を検証しながら進める領域です。

3. ログを有効にする ── 4104が最重要

PowerShellのログには3つの階層があります。1

種類 記録内容 イベントID
モジュールログ 指定モジュールのパイプライン実行の詳細 4103
スクリプトブロックログ 実行されたコードのテキスト(難読化解除後) 4104
記録先のログ 5.1は Microsoft-Windows-PowerShell/Operational、7は PowerShellCore/Operational
トランスクリプション セッションの入出力をテキストファイルに記録 ─(ファイル出力)

いずれもグループポリシーの「コンピューターの構成 > 管理用テンプレート > Windows コンポーネント > Windows PowerShell」で設定できます。レジストリで直接設定することも可能です。1

# スクリプトブロックログを有効化する(要管理者権限。通常はGPOで配布する)
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'
New-Item -Path $key -Force | Out-Null
# -Type はレジストリプロバイダーが追加する動的パラメーター。値の型(DWord)を明示できる
Set-ItemProperty -Path $key -Name 'EnableScriptBlockLogging' -Value 1 -Type DWord

# PowerShell 7(pwsh)は別のポリシーキーを見る。両方に設定する
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\PowerShellCore\ScriptBlockLogging'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableScriptBlockLogging' -Value 1 -Type DWord

# トランスクリプションを有効化し、書き込み専用の共有へ集約する
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableTranscripting'     -Value 1 -Type DWord
Set-ItemProperty -Path $key -Name 'OutputDirectory'         -Value '\\logsrv\pstranscripts$'
Set-ItemProperty -Path $key -Name 'EnableInvocationHeader'  -Value 1 -Type DWord

# トランスクリプションもPowerShell 7側は別キー。同じ値を書く
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\PowerShellCore\Transcription'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableTranscripting'     -Value 1 -Type DWord
Set-ItemProperty -Path $key -Name 'OutputDirectory'         -Value '\\logsrv\pstranscripts$'
Set-ItemProperty -Path $key -Name 'EnableInvocationHeader'  -Value 1 -Type DWord

PowerShell 7は Windows\PowerShell 配下ではなく PowerShellCore 配下のポリシーを見ます。5.1側だけ設定して満足すると、pwshで実行されたスクリプトの記録がまるごと欠落します。スクリプトブロックログ・トランスクリプションとも、両方のキーに同じ値を書いてください(GPOで配布する場合も、それぞれのテンプレートで設定します)。

スクリプトブロックログの価値は、難読化への耐性にあります。Base64でエンコードされたコマンドや、文字列連結で組み立てられたコードであっても、実行時点で展開された内容が記録されます。1 攻撃の調査で「何が実行されたか」を再現できるかどうかは、この設定の有無で決まります。

記録の確認は Get-WinEvent です(「Get-WinEventでイベントログを実務的に調べる」)。

# 直近1日のスクリプトブロックログを確認する。
# Windows PowerShell(5.1)とPowerShell 7では書き込まれるログが別なので、
# 両方を対象にする
Get-WinEvent -FilterHashtable @{
    LogName   = 'Microsoft-Windows-PowerShell/Operational', 'PowerShellCore/Operational'
    ID        = 4104
    StartTime = (Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue |
    Select-Object TimeCreated, LogName, @{ n='Script'; e={ $_.Message } } -First 20

有効化したら必ずログサイズを見直してください。既定サイズのままだと数時間〜数日で一巡し、いざというときに残っていません。

Get-WinEvent -ListLog 'Microsoft-Windows-PowerShell/Operational', 'PowerShellCore/Operational' |
    Select-Object LogName, MaximumSizeInBytes, RecordCount
wevtutil sl Microsoft-Windows-PowerShell/Operational /ms:1073741824   # 1GBに拡張する例
wevtutil sl PowerShellCore/Operational               /ms:1073741824   # PowerShell 7側も忘れずに

トランスクリプトの出力先は、利用者が書き込みだけできて削除・上書きできない共有にするのが要点です。ローカルに置くと、侵害された端末では消されてしまいます。

4. 迂回経路を塞ぐ ── PowerShell 2.0とAMSI

AMSI(Antimalware Scan Interface) により、PowerShell 5.0以降ではスクリプトの内容が実行直前にウイルス対策製品へ渡され、難読化を解除した状態で検査されます。2 Microsoft Defenderを含む対応製品を使っていれば、追加設定なしで機能します。

問題は、古いエンジンにはこの仕組みがないことです。Windows PowerShell 2.0エンジンが有効なままだと、powershell.exe -Version 2 によってログもAMSIも効かない環境に切り替えられる余地が残ります。この機能は非推奨であり、無効化が推奨されています。3

# PowerShell 2.0エンジンの状態を確認し、無効化する(要管理者権限・再起動が必要な場合あり)
Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2* |
    Select-Object FeatureName, State

Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root -NoRestart

なお、PowerShell 7(pwsh)を導入している場合、設定キーもログの記録先も5.1とは別です(ポリシーは PowerShellCore 配下、ログは PowerShellCore/Operational)。両方が使われる環境では、設定・ログサイズ・調査クエリのすべてを両方に対して行ってください。バージョンの共存については「Windows PowerShell 5.1とPowerShell 7の違い」を参照してください。

5. 実行ポリシーの位置づけを間違えない

改めて明確にしておきます。実行ポリシーはセキュリティ境界ではありません。公式ドキュメントは、ユーザーが意図せずスクリプトを実行してしまうことを防ぐための安全機能であり、悪意ある操作を防ぐものではないと明記しています。4

とはいえ無価値ではありません。署名運用には「配布したモジュールが改ざんされていないことを確認できる」という別の価値があります(「PowerShellモジュールの社内配布と更新」)。役割を正しく理解して使うことが重要で、「AllSignedにしたから安全」という理解が最も危険です。

6. 言語モード ── アプリケーション制御と組み合わせて使う

PowerShellのセッションには言語モードがあり、使える言語要素が制限されます。5

モード 使えるもの
FullLanguage すべて(既定)
ConstrainedLanguage 承認された型のみ。任意の.NET型の呼び出しなどが制限される
RestrictedLanguage コマンドは実行できるがスクリプトブロックは不可
NoLanguage コマンドの実行のみ(APIからの利用)

現在のモードは次で確認できます。

$ExecutionContext.SessionState.LanguageMode

重要なのは設定方法です。制約付き言語モードは、WDAC(Windows Defender Application Control)やAppLockerで許可リスト方式のアプリケーション制御を構成した際に、PowerShellが自動的に切り替わる形で機能します。5 環境変数などで手動設定する方法は回避が容易であり、セキュリティ機能としては機能しません。アプリケーション制御を導入せずに言語モードだけを絞るのは、労力の割に効果がないと理解してください。

7. JEA ── 「必要な操作だけ」を委任する

現実の被害を左右するのは、多くの場合権限の広さです。「ヘルプデスクに管理者権限を渡している」「運用担当が全員Domain Adminに入っている」という状態では、1台の侵害が全社の侵害になります。

JEA(Just Enough Administration)は、管理者権限を渡さずに特定の操作だけを委任する仕組みです。6 「アプリケーションサービスの再起動だけをヘルプデスクに任せる」といった要求に、ぴったり合います。

手順1: 役割機能ファイル(.psrc)で許可する操作を定義する

役割機能ファイルは、PowerShellモジュールの RoleCapabilities フォルダーに置かれている必要があります。フォルダーを作っただけではモジュールとして認識されず、後述の RoleDefinitions から名前で解決できません。そのため、まず入れ物となるモジュール(マニフェストを持つフォルダー)を作ります。6

# 役割機能を入れるモジュールを作る(マニフェストが必要)
$moduleRoot = 'C:\Program Files\WindowsPowerShell\Modules\KsJea'
$null = New-Item -Path "$moduleRoot\RoleCapabilities" -ItemType Directory -Force
# モジュールフォルダーには、フォルダーと同名のファイルが1つ以上必要
$null = New-Item -Path "$moduleRoot\KsJea.psm1" -ItemType File -Force
New-ModuleManifest -Path "$moduleRoot\KsJea.psd1" -RootModule 'KsJea.psm1'

# 役割機能ファイルの雛形を作る(ファイル名が役割名になる)。
# 役割名は PSModulePath 上の全モジュールから「名前だけ」で解決されるため、
# 'HelpDesk' のような一般的な名前は他のモジュールの .psrc と衝突しうる。
# 衝突するとどちらが選ばれるかの保証がなく、意図しない権限が与えられる。
# 組織の接頭辞を付けた一意な名前にする
New-PSRoleCapabilityFile -Path "$moduleRoot\RoleCapabilities\KsHelpDesk.psrc"

# モジュールとして見えているかを確認する
Get-Module -Name KsJea -ListAvailable
# KsHelpDesk.psrc(抜粋)── 「何を、どこまで」許可するかを宣言する
@{
    GUID           = '....'
    # 読み取り専用のコマンドはそのまま見せてよい
    VisibleCmdlets = @(
        'Get-Service',
        'Get-EventLog'
    )
    # 状態を変える Restart-Service は、あえて VisibleCmdlets に入れない(後述)
    # VisibleFunctions は「セッションに読み込まれている関数」を絞り込むだけで、
    # 関数を定義はしない。独自関数は FunctionDefinitions で中身を書き、
    # そのうえで VisibleFunctions にも名前を挙げる(両方必要)
    VisibleFunctions = @('Get-KsAppStatus', 'Restart-KsAppService')
    FunctionDefinitions = @(
        @{
            Name        = 'Get-KsAppStatus'
            ScriptBlock = {
                # 関数の本体は既定の言語モードで動くため、JEAの制約を受けない。
                # 利用者の入力をそのまま危険なコマンドへ渡さないこと
                Get-Service -Name 'KsAppService' |
                    Microsoft.PowerShell.Utility\Select-Object Name, Status, StartType
            }
        },
        @{
            # 再起動は「引数でしか対象を受け取らない」関数として公開する
            Name        = 'Restart-KsAppService'
            ScriptBlock = {
                param(
                    [Parameter(Mandatory)]
                    [ValidateSet('KsAppService', 'Spooler')]
                    [string] $Name
                )
                Microsoft.PowerShell.Management\Restart-Service -Name $Name
            }
        }
    )
    VisibleExternalCommands = @()
}

VisibleCmdletsValidateSet だけでは、状態を変えるコマンドを安全に絞れません。ParametersValidateSet による制限は、そのパラメーターが実際に束縛されたときにしか評価されませんRestart-ServiceServiceController をパイプラインで受け取れるため、

Get-Service WinRM | Restart-Service

と書かれると -Name が束縛されず、ValidateSet は素通りします。「KsAppServiceSpooler だけ再起動できる」つもりのエンドポイントで、任意のサービスを再起動できてしまうわけです。

同じことは別のパラメーターセットでも起きます。Get-WinEventLogNameValidateSet を付けても、

Get-WinEvent -ProviderName Microsoft-Windows-Security-Auditing -MaxEvents 1

と書かれれば LogName は束縛されず、制限は働きません。JEAセッションは仮想管理者として動くため、この場合Securityログまで読めてしまいます。

そのため上の例では Restart-ServiceVisibleCmdlets に入れず、引数でしか対象を受け取らないラッパー関数(Restart-KsAppService)だけを公開しています。入力経路が引数しかなければ、迂回のしようがありません。

ParametersValidateSet による制限は、「そのパラメーターが束縛されたとき」にしか働きません。パイプライン入力や別のパラメーターセットで束縛を回避できるコマンドでは、制限そのものが無効になります。引数を絞りたいコマンドは、原則としてラッパー関数で包むと考えてください。

ここで2点、はまりやすい仕様があります。1つ目は、VisibleFunctions は関数を作らないことです。名前を挙げただけの関数はセッションに存在せず、利用者には「そんなコマンドはない」と見えます。独自関数は FunctionDefinitions で定義したうえで、VisibleFunctions にも挙げてください。7 数が増えるならスクリプトモジュールに切り出し、そのモジュールの関数を VisibleFunctions で公開するほうが管理しやすくなります。

2つ目は、関数の本体がJEAの制約を受けないことです。7 Select-Object などJEAが差し替える制約付きコマンドを本来の挙動で使いたい場合は、上のように Microsoft.PowerShell.Utility\Select-Object と完全修飾で呼びます。裏を返せば、関数の中では何でもできるということなので、利用者からの入力を Invoke-Expression に渡すような書き方は絶対に避けてください。

手順2: セッション構成ファイル(.pssc)で、誰にどの役割を割り当てるかを定義する

# SessionType: 制限付きリモートサーバー(既定でNoLanguage)
# RunAsVirtualAccount: 仮想管理者アカウントとして実行
# TranscriptDirectory: 実行内容を記録
$pssc = @{
    Path                = '.\KsHelpDesk.pssc'
    SessionType         = 'RestrictedRemoteServer'
    RunAsVirtualAccount = $true
    TranscriptDirectory = 'C:\JeaTranscripts'
    RoleDefinitions     = @{ 'EXAMPLE\HelpDesk' = @{ RoleCapabilities = 'KsHelpDesk' } }
}
New-PSSessionConfigurationFile @pssc

手順3: 登録する

Register-PSSessionConfiguration -Name 'KsHelpDesk' -Path .\KsHelpDesk.pssc -Force

利用者側は、次のように接続します。

Enter-PSSession -ComputerName 'appsrv01' -ConfigurationName 'KsHelpDesk'
# 許可されたコマンドしか使えない。Restart-Service も指定サービスのみ

JEAの要点は3つです。6

  • 利用者は管理者権限を持たない。実行は仮想アカウント側で行われる
  • セッションは制限付きリモートサーバーとして構成される。既定で言語モードが制限され、任意のコードを実行できない
  • トランスクリプトで実行内容が記録される。誰が何をしたかが監査できる

なお、役割機能ファイルは前述のとおりモジュールの RoleCapabilities フォルダー配下にある必要があり、そのモジュールが $env:PSModulePath から見つかることが前提です。RoleDefinitions で指定した役割名が解決できないときは、まず Get-Module -ListAvailable でモジュールが見えているかを確認してください。6

導入にはリモート実行の構成が前提になります(「PowerShell Remoting(WinRM)入門」)。

8. 実務チェックリスト

項目 優先度 状態
スクリプトブロックログ(4104)を有効化した GPOで全社配布18
PowerShell関連ログのサイズを拡張した 既定のままだと数日で消える
トランスクリプションを有効化し、書き込み専用共有に集約した 端末ローカルに置かない1
PowerShell 2.0エンジンを無効化した ログ・AMSIの迂回経路3
ウイルス対策製品がAMSIに対応している 既定で有効2
実行ポリシーをセキュリティ境界と誤解していない 署名は別の価値4
管理者権限の付与範囲を棚卸しした 被害範囲を決めるのは権限の広さ
定型作業をJEAで委任した 管理者権限の削減に直結6
WDAC/AppLockerを検討した 言語モード制限はこれとセット5
PowerShell 7側にもログ設定を配布した 5.1と7で設定は別

9. まとめ

  • PowerShellの禁止は実効性が低く、業務を止めます。方針は「可視化と制限」です。
  • 最優先はスクリプトブロックログ(4104)の有効化。難読化されたコードも展開後の形で記録されます。ログサイズの拡張とセットで実施してください。
  • トランスクリプションは、利用者が消せない共有に集約するのが要点です。
  • PowerShell 2.0エンジンは無効化します。ログもAMSIも効かない経路を残さないためです。
  • 実行ポリシーはセキュリティ境界ではありません。署名は改ざん検知として価値がありますが、防御の主軸にはしないでください。
  • 権限の広さが被害の広さを決めます。JEAで「必要な操作だけ」を委任すれば、管理者権限を配らずに運用が回せます。

サンプルコードのダウンロード

この記事で扱ったコードは、そのまま動かせる形にまとめて配布しています。ログ設定の有効化と、JEAエンドポイントの作成が入っています。

サンプルコードをダウンロード(zip)

この記事のサンプルは、Windowsやテナントに依存するため実行検証はしていません。構文解析とPSScriptAnalyzerによる静的解析までは全ファイルに対して実施していますが、動作は必ずご自身の検証機で確認してください。

# 構文解析 + 静的解析(Windows以外でも実行できる)
./Invoke-SampleTests.ps1

設定値(パス、サーバー名、テナントIDなど)は例です。そのまま本番環境で実行せず、自社の環境に合わせて読み替えてください。

関連記事

関連する相談領域

合同会社小村ソフトでは、Windows運用環境のセキュリティ設定レビュー、権限委任(JEA)を含む運用設計、監査ログの整備と活用の相談を扱っています。

参考リンク

  1. Microsoft Learn, about_Logging_Windows. モジュールログ・スクリプトブロックログ・トランスクリプションの3種類のログ機能、グループポリシーおよびレジストリ(HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell配下)による有効化、スクリプトブロックログがイベントID 4104として難読化解除後のコードを記録すること、モジュールログがイベントID 4103として記録されること、トランスクリプションのOutputDirectoryやEnableInvocationHeaderの設定について。  2 3 4 5 6 7 8

  2. Microsoft Learn, Antimalware Scan Interface (AMSI). AMSIがアプリケーションとサービスから任意のアンチマルウェア製品へコンテンツを渡して検査させる仕組みであること、PowerShellを含むWindowsのスクリプトエンジンが統合されており、難読化されたスクリプトも実行時の内容で検査できることについて。  2 3

  3. Microsoft Learn, Windows PowerShell 2.0 の非推奨化. Windows PowerShell 2.0エンジンが非推奨であり、無効化が推奨されること、Windowsのオプション機能として提供され有効・無効を切り替えられることについて。  2 3

  4. Microsoft Learn, about_Execution_Policies. 実行ポリシーがセキュリティ境界ではなく、ユーザーが意図せずスクリプトを実行することを防ぐための安全機能であること、複数の回避手段が存在することについて。  2 3

  5. Microsoft Learn, about_Language_Modes. FullLanguage・ConstrainedLanguage・RestrictedLanguage・NoLanguageの各モードで利用できる言語要素、$ExecutionContext.SessionState.LanguageModeによる現在のモードの確認、WDACやAppLockerによるアプリケーション制御が有効な環境でPowerShellが制約付き言語モードで動作すること、手動での言語モード設定がセキュリティ機能として意図されていないことについて。  2 3 4

  6. Microsoft Learn, Just Enough Administration (JEA) の概要. JEAが管理者権限を付与せずに特定の管理作業のみを委任する仕組みであること、役割機能ファイル(.psrc)によるVisibleCmdlets・パラメーターとValidateSetによる制限、セッション構成ファイル(.pssc)のRestrictedRemoteServer・RunAsVirtualAccount・TranscriptDirectory・RoleDefinitions、Register-PSSessionConfigurationによる登録、JEAセッションで言語モードが制限されることについて。役割機能ファイルをPowerShellモジュールのRoleCapabilitiesフォルダーに配置する必要があること(モジュールとして検出できる状態にしておくこと、New-PSRoleCapabilityFileによる作成)はJEA Role Capabilitiesを参照。  2 3 4 5 6

  7. Microsoft Learn, JEA Role Capabilities. 独自関数をFunctionDefinitionsで定義したうえでVisibleFunctionsにも名前を挙げる必要があること(「Don’t forget to add the name of your custom functions to the VisibleFunctions field so they can be run by the JEA users.」)、関数の本体(スクリプトブロック)がシステム既定の言語モードで実行されJEAの制約を受けないこと、JEAが差し替える制約付きコマンドを本来の実装で使うには完全修飾名(Microsoft.PowerShell.Utility\Select-Object)が必要なこと、関数が多い場合はスクリプトモジュールに切り出してVisibleFunctionsで公開する方法、モジュールフォルダーにフォルダーと同名のファイルが必要なことについて。  2

  8. Microsoft Learn, Set-ItemProperty. レジストリプロバイダー使用時に-Typeが動的パラメーターとして追加され、レジストリ値のデータ型(String / ExpandString / Binary / DWord / MultiString / QWord など)を指定できることについて。値が存在しない場合に作成されることを含む。 

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

セキュリティ対策として、社内でPowerShellを禁止すべきでしょうか?
実効性が低く、副作用が大きいため推奨しません。PowerShellはWindowsの管理基盤そのもので、実体は.NET上の機能です。実行ファイルをブロックしても、同じAPIを別の手段から呼ぶことは可能なため、攻撃側にとっての障害にはなりにくい一方、正規の管理業務と自動化は確実に止まります。現実的な方針は、禁止ではなく「可視化と制限」です。スクリプトブロックログで何が実行されたかを記録し、古いバージョンを無効化し、必要に応じて言語モードやJEAで実行できる範囲を絞ります。
スクリプトブロックログを有効にすると、ログが大量に出て困りませんか?
確かに増えるので、ログのサイズ設定とセットで有効化してください。Microsoft-Windows-PowerShell/Operationalログの最大サイズを既定のままにしておくと、短期間で上書きされて肝心なときに残っていない、という事態になります。運用としては、ログサイズを十分に確保し、必要ならSIEMやイベント転送で集約します。なお、より詳細な「呼び出しの開始・停止」まで記録する設定もありますが、こちらは出力量が非常に多いため、通常は既定のスクリプトブロックログのみを有効にします。
実行ポリシーをAllSignedにすれば、セキュリティ対策になりますか?
実行ポリシーはセキュリティ境界ではありません。公式ドキュメントも、ユーザーが意図せず危険なスクリプトを実行することを防ぐための仕組みであって、悪意ある操作を止めるものではないと明記しています。回避方法が複数存在するためです。署名運用には、配布物の完全性を確認できるという別の価値がありますが、防御の主軸はログによる可視化、AMSIによる検査、言語モードやJEAによる権限の制限に置いてください。
制約付き言語モード(ConstrainedLanguage)は、手動で設定してよいものですか?
環境変数などで手動設定するのは推奨されません。回避が容易で、セキュリティ機能として機能しないためです。制約付き言語モードは、WDAC(Windows Defender Application Control)やAppLockerによるアプリケーション制御を構成した結果として、PowerShellが自動的に切り替わる形で使うのが本来の設計です。アプリケーション制御を導入せずに言語モードだけを絞っても、実効性のある防御にはなりません。
ヘルプデスク担当者に、サーバーの特定サービスの再起動だけを任せたいのですが。
JEA(Just Enough Administration)がその用途のための仕組みです。役割機能ファイルで「このコマンドの、このパラメーターの、この値だけ許可する」と定義し、セッション構成として登録します。利用者は管理者権限を持たないまま、仮想アカウント経由で許可された操作だけを実行できます。セッションは制限付きリモートサーバーとして構成され、既定では言語モードも制限されるため、任意のコードを実行される余地がありません。実行内容はトランスクリプトとして記録できます。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る