次の方法で共有


スクリプト インジェクション攻撃の防止

PowerShell スクリプトは、他のプログラミング言語と同様に、インジェクション攻撃に対して脆弱になる可能性があります。 インジェクション攻撃は、ユーザーが追加のコマンドを含む脆弱な関数に入力を提供したときに発生します。 脆弱な関数は追加のコマンドを実行します。これは重大なセキュリティの脆弱性になる可能性があります。 たとえば、悪意のあるユーザーが脆弱な機能を悪用してリモート コンピューター上で任意のコードを実行し、そのコンピューターを侵害し、ネットワーク上の他のマシンにアクセスする可能性があります。

問題を認識したら、インジェクション攻撃から保護するいくつかの方法があります。

脆弱なコードの例

PowerShell コードインジェクションの脆弱性には、スクリプト コードを含むユーザー入力が含まれます。 ユーザー入力は、PowerShell によって解析および実行される脆弱なスクリプトに追加されます。

function Get-ProcessById
{
    param ($ProcId)

    Invoke-Expression -Command "Get-Process -Id $ProcId"
}

Get-ProcessById関数は、ID 値によってローカル プロセスを検索します。 任意の型の $ProcId パラメーター引数を受け取ります。 その後、 $ProcId は文字列に変換され、 Invoke-Expression コマンドレットを使用して解析されて実行される別のスクリプトに挿入されます。 この関数は、有効なプロセス ID 整数が渡されると正常に動作します。

Get-ProcessById $PID

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     97    50.09     132.72       1.20   12528   3 pwsh

ただし、 $ProcId パラメーターは型を指定しません。 他のコマンドを含めることができる任意の文字列値を受け入れます。

Get-ProcessById "$PID; Write-Host 'pwnd!'"

この例では、関数は $PIDによって識別されたプロセスを正しく取得しましたが、挿入されたスクリプト Write-Host 'pwnd!'も実行しました。

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     92    45.66     122.52       1.06   21736   3 pwsh
pwnd!

インジェクション攻撃から保護する方法

インジェクション攻撃から保護するには、いくつかの方法があります。

入力はタイプ済みのものを使用する

$ProcId引数の型を指定できます。

function Get-ProcessById
{
    param ([int] $ProcId)

    Invoke-Expression -Command "Get-Process -Id $ProcId"
}
Get-ProcessById "$PID; Write-Host 'pwnd!'"
Get-ProcessById:
Line |
   7 |  Get-ProcessById "$PID; Write-Host 'pwnd!'"
     |                  ~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot process argument transformation on parameter 'ProcId'. Cannot convert value
"8064; Write-Host 'pwnd!'" to type "System.Int32". Error: "The input string '8064; Write-Host 'pwnd!'
was not in a correct format."

ここでは、 $ProcId 入力パラメーターは整数型に制限されているため、整数に変換できない文字列が渡されるとエラーが発生します。

Invoke-Expression は使用しません

Invoke-Expressionを使用する代わりに、Get-Processを直接呼び出し、PowerShell のパラメーター バインダーで入力を検証します。

function Get-ProcessById
{
    param ($ProcId)

    Get-Process -Id $ProcId
}
Get-ProcessById "$PID; Write-Host 'pwnd!'"
Get-Process:
Line |
   5 |      Get-Process -Id $ProcId
     |                      ~~~~~~~
     | Cannot bind parameter 'Id'. Cannot convert value "8064; Write-Host 'pwnd!'" to type
"System.Int32". Error: "The input string '8064; Write-Host 'pwnd!' was not in a correct
format."

ベスト プラクティスとして、特にユーザー入力を処理する場合は、 Invoke-Expressionの使用を避ける必要があります。 Invoke-Expression は、指定した文字列コンテンツを解析して実行し、インジェクション攻撃に対して脆弱になるため、危険です。 PowerShell パラメーター バインドに依存することをお勧めします。

文字列を一重引用符で囲む

ただし、 Invoke-Expression を使用することは避けられない場合があり、ユーザー文字列の入力も処理する必要があります。 各文字列入力変数を囲む単一引用符を使用して、ユーザー入力を安全に処理できます。 一重引用符を使用すると、PowerShell のパーサーがユーザー入力を単一の文字列リテラルとして扱うことができます。

function Get-ProcessById
{
    param ($ProcId)

    Invoke-Expression -Command "Get-Process -Id '$ProcId'"
}

Get-ProcessById "$PID; Write-Host 'pwnd!'"
Get-Process: Cannot bind parameter 'Id'. Cannot convert value "8064; Write-Host " to type
"System.Int32". Error: "The input string '8064; Write-Host' was not in a correct format."

ただし、このバージョンの関数はまだインジェクション攻撃から完全に安全ではありません。 悪意のあるユーザーは、入力で引き続き単一引用符を使用してコードを挿入できます。

Get-ProcessById "$PID'; Write-Host 'pwnd!';'"

この例では、ユーザー入力で単一引用符を使用して、関数で 3 つの個別のステートメントを強制的に実行します。そのうちの 1 つは、ユーザーによって挿入された任意のコードです。

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     97    46.08     183.10       1.08    2524   3 pwsh
pwnd!

EscapeSingleQuotedStringContent() メソッドを使用する

関数を利用するために独自の単一引用符文字を挿入するユーザーから保護するには、 EscapeSingleQuotedStringContent() API を使用する必要があります。 これは、PowerShell System.Management.Automation.Language.CodeGeneration クラスの静的パブリック メソッドです。 このメソッドは、ユーザー入力に含まれる単一引用符をエスケープすることで、ユーザー入力を安全にします。

function Get-ProcessById
{
    param ($ProcId)

    $ProcIdClean = [System.Management.Automation.Language.CodeGeneration]::
        EscapeSingleQuotedStringContent("$ProcId")
    Invoke-Expression -Command "Get-Process -Id '$ProcIdClean'"
}
Get-ProcessById "$PID'; Write-Host 'pwnd!';'"
Get-Process: Cannot bind parameter 'Id'. Cannot convert value "8064'; Write-Host 'pwnd!';'" to type
"System.Int32". Error: "The input string '8064'; Write-Host 'pwnd!';'' was not in a correct format."

詳細については、「 EscapeSingleQuotedStringContent()」を参照してください。

インジェクション ハンターによる脆弱なコードの検出

インジェクション ハンター は Lee Holmes によって記述されたモジュールで、コードインジェクションの脆弱性を検出するための PowerShell スクリプト アナライザールールが含まれています。 PowerShell ギャラリーからモジュールをインストールするには、次のいずれかのコマンドを使用します。

# Use PowerShellGet v2.x
Install-Module InjectionHunter

# Use PowerShellGet v3.x
Install-PSResource InjectionHunter

これを使用して、ビルド中のセキュリティ分析、継続的インテグレーション プロセス、デプロイ、およびその他のシナリオを自動化できます。

$RulePath = (Get-Module -List InjectionHunter).Path
Invoke-ScriptAnalyzer -CustomRulePath $RulePath -Path .\Invoke-Dangerous.ps1
RuleName                            Severity     ScriptName Line  Message
--------                            --------     ---------- ----  -------
InjectionRisk.InvokeExpression      Warning      Invoke-Dan 3     Possible script injection risk via the
                                                 gerous.ps1       Invoke-Expression cmdlet. Untrusted input can cause
                                                                  arbitrary PowerShell expressions to be run.
                                                                  Variables may be used directly for dynamic parameter
                                                                  arguments, splatting can be used for dynamic
                                                                  parameter names, and the invocation operator can be
                                                                  used for dynamic command names. If content escaping
                                                                  is truly needed, PowerShell has several valid quote
                                                                  characters, so  [System.Management.Automation.Languag
                                                                  e.CodeGeneration]::Escape* should be used.

詳細については、「 PSScriptAnalyzer」を参照してください。