【问题标题】:Powershell - if statement operator through paramenterPowershell - 通过参数的if语句运算符
【发布时间】:2022-07-19 00:03:55
【问题描述】:

您好 Powershell 专家, 我有一个关于我的 powershell 问题的简短问题。

我尝试在 if 语句中传递参数变量来处理这些变量。我的目标是拥有一个完全动态的 if 语句。 让我告诉你情况:

function Get-Test {

    param(
        [parameter(Mandatory=$true)]
         [ValidateNotNullOrEmpty()]$varA,
         [parameter(Mandatory=$true)]
         [ValidateNotNullOrEmpty()]$varB,
         [parameter(Mandatory=$true)]
         [ValidateNotNullOrEmpty()]$op
     )
     $statement = "$varA $op $varB"

     if ($statement) {
        Write-Host "One"

     } else {
        Write-Host "Two"
     }

}

Get-Test -varA "Test1" -varB "Test1" -op "-ne"

说明: 无论我输入什么作为参数 $op 它总是会变成“一”

所以我的问题是:是否有可能使用参数/变量在我的 if 语句中使用某种动态运算符?

【问题讨论】:

  • $statement = IEX "'$varA' $op '$varB'" 应该可以,但如果你解释你的最终目标是什么,很可能有更好的方法来解决这个问题
  • @SantiagoSquarzon - 我希望你已经知道这一点,但是如果$op = "; Format-Volume -DriveLetter C ;" 为 OP 的利益,它将给出一些非常意想不到的结果:-)。
  • @mclayton 我和你在一起你知道但有时人们不想听:)
  • 非常感谢@SantiagoSquarzon! Invoke-Expression 的想法很棒。在我的情况下就像一个魅力。

标签: powershell if-statement variables parameters operators


【解决方案1】:

根据@SantiagoSquarzon 的 cmets,一种方法是使用Invoke-Expression 生成包含 PowerShell 命令的字符串,然后执行它:

function Get-Test {

    param(
        [parameter(Mandatory=$true)]
         [ValidateNotNullOrEmpty()]$varA,
         [parameter(Mandatory=$true)]
         [ValidateNotNullOrEmpty()]$varB,
         [parameter(Mandatory=$true)]
         [ValidateNotNullOrEmpty()]$op
     )

     $statement = IEX "'$varA' $op '$varB'"

     if ($statement) {
        Write-Host "One"

     } else {
        Write-Host "Two"
     }

}

Get-Test -varA "Test1" -varB "Test1" -op "-ne"

但是,您应该非常小心地验证您的输入(尤其是如果它是像用户界面这样的“不可信”来源),因为它包含与 SQL 注入攻击相同类型的问题 - 也就是说,您最终可能会运行函数中的任意代码。

例如,如果 $op 以某种方式以 "; write-host 'i didn''t mean to run this' ;" 的值结束(例如,来自未经处理的用户输入或尖峰输入文件),您的函数将执行 write-host 命令没有任何错误所以您的输出将如下所示:

i didn't mean to run this
One

这本身可能并没有那么糟糕,但是如果字符串中有更恶意的东西怎么办 - 例如。 "; Format-Volume -DriveLetter C ;" - 你真的想在你的服务器上执行那个命令吗?

解决此问题的一种方法是列出您将支持的已知操作 - 预先需要做更多工作,但可以避免 Invoke-Expression 的安全问题:

function Get-Test {

    param(
        [parameter(Mandatory=$true)]
         [ValidateNotNullOrEmpty()]$varA,
         [parameter(Mandatory=$true)]
         [ValidateNotNullOrEmpty()]$varB,
         [parameter(Mandatory=$true)]
         [ValidateNotNullOrEmpty()]$op
    )

    $result = switch( $op )
    {
        "-eq" { $varA -eq $varB }
        "-lt" { $varA -lt $varB }
        "-gt" { $varA -gt $varB }
        # etc for any other operations you want to support
        default {
            throw "invalid operation '$op"
        }
    }

    if ($result) {
        Write-Host "One"

    } else {
       Write-Host "Two"
    }

}

如果您尝试使用无效操作,您将得到如下结果:

PS> Get-Test -varA "Test1" -varB "Test1" -op "; write-host 'i didn't mean to run this' ;"
Exception:
Line |
  19 |              throw "invalid operation '$op"
     |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | invalid operation '; write-host 'i didn't mean to run this' ;

【讨论】:

    【解决方案2】:

    您的函数不会尝试评估可扩展字符串"$varA $op $varB"的值作为代码。

    鉴于 PowerShell 中的任何非空字符串 在布尔上下文中都被视为 $true,因此 if ($statement)总是为真。

    虽然Invoke-Expression 允许您将字符串评估为 PowerShell 代码,但它带有重要的警告:

    • 一般而言,它会使您的代码容易受到不需要的命令的注入(请参阅mclayton 对该问题的评论以获取示例)。

      • 简而言之:只有在您完全控制输入字符串或隐式信任给定的字符串时,才使用Invoke-Expression。通常,可以使用比 Invoke-Expressions 更优、更安全的替代方案 - 请参阅 this answer 了解更多信息。
    • 具体而言,在您的情况下,将$varA 和$varB 原样嵌入expandable (double-quoted) string ("...") 意味着通过Invoke-Expression 进行的评估对于字符串 参数值以及通常对于值而言将失败的 .NET 类型没有有意义的字符串表示。


    因此,请执行以下操作:

    • 为防止代码注入,请使用 PowerShell 的语言解析器检查 $op 以确保它真正代表 PowerShell操作员。

    • 不使用Invoke-Expression,而是从可扩展字符串构造script block,并将$varA 和$varB 作为参数传递给该脚本块,以确保使用它们的值按原样(而不是进行字符串化)。

    function Get-Test {
    
      param(
        [parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]$varA,
        [parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]$varB,
        [parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]$op
      )
    
      # IMPORTANT: To prevent code injection, ensure that $op really refers to
      #            a PowerShell operator.
      # Note: '-not' is a dummy operator that forces $op to be parsed in expression mode.
      $tokenKind = (
        [System.Management.Automation.PSParser]::Tokenize("-not $op", [ref] $null) |
          Select-Object -Skip 1
      ).Type
    
      if ('Operator' -ne $tokenKind) {
        Throw "'$op' is not a PowerShell operator; it is a token of type $tokenKind"
      }
    
      # Create a script block based on the operator, and pass the operands (parameters)
      # *as arguments* to the script block.
      & ([scriptblock]::Create("`$args[0] $op `$args[1]")) $varA $varB
    
    }
    

    【讨论】:

      猜你喜欢
      • 2017-10-26
      • 2022-01-09
      • 2015-02-08
      • 2015-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多