如何获得ReadLineAsSecureString()接受管道输入?

时间:2018-09-17 15:12:11

标签: windows powershell cmd

当我使用cmd并将管道输送到使用powershell来读取输入的ReadLine()时,它会按预期接受管道输入:

C:\Users\ohnob>ECHO hi|powershell -Command "write-host $host.UI.ReadLine()"
hi
hi

但是,当使用cmd并使用powershell管道连接到ReadLineAsSecureString()时,它将一直挂起,直到我在终端中键入return为止:

C:\Users\ohnob>ECHO hi|powershell -Command "write-host $host.UI.ReadLineAsSecureString()"

当会话是交互式的时,我需要阅读为安全字符串(使用星号)。但是我需要在有管道输入时读取管道输入。如何在powershell中完成此操作?我希望能够以某种方式检测到stdin是否是管道,然后有条件地使用ReadLine()而不是ReadLineAsSexcureString(),但是我不知道如何在powershell中获取标准输入的句柄-我只能访问此PSHostUserInterface对象。

编辑:为明确起见,当我使用ReadLineAsSecureString()ReadLine()时,我希望它从输入中读取一行。这样就可以多次使用它,并且用户可以为脚本提供多个值。我希望所有答案都能代替我使用的功能,除非没有我要避免的问题。谢谢!

1 个答案:

答案 0 :(得分:1)

Jeroen Mostert在注释中提出了一些要点,尤其是$host.UI.ReadLineAsSecureString() / Read-Host -AsSecureString 大概 设计为不接受管道输入,出于安全原因 [1]

因此,您必须明确区分接收管道输入和不接收任何管道输入:

  • PowerShell通过自动$Input变量提供对从外部管道输入的stdin输入的访问。

  • $MyInvocation.ExpectingInput通常指示是否存在管道输入。

如果有管道输入,请将其第一行 [2] 传递到ConvertTo-SecureString;
如果没有,请致电Read-Host -AsSecureString

$secStr = if ($MyInvocation.ExpectingInput) {
    # Alternatively, use $firstLine = [console]::ReadLine() - thanks, @binki
    $firstLine = $($null = $Input.MoveNext(); $Input.Current)
    ConvertTo-SecureString -AsPlainText -Force $firstLine
  } else {
    Read-Host -AsSecureString -Prompt 'Enter secret'
  }

作为要从cmd.exe进行调用的命令行,它还会输出结果:

C:>echo hi| powershell -Command "$secStr = if ($MyInvocation.ExpectingInput) { $firstLine = $($null = $Input.MoveNext(); $Input.Current); ConvertTo-SecureString -AsPlainText -Force $firstLine } else { Read-Host -AsSecureString -Prompt 'Enter secret' }l$secStr"

不过,请注意,安全字符串在设计上通常会以System.Security.SecureString的形式打印,这就是您所看到的全部内容。


[1]我正在推测,而没有有关这方面的文档。将{<1>}的纯文本字符串 Piping 肯定具有不安全的更大的潜力:通过管道传输的纯文本字符串可以这样保存在某个地方,并且至少假设,如果使用Read-Host -AsSecureString之类的东西,则该进程的命令行将反映该机密信息。
无论哪种方式,将纯文本传递到MyCustomEcho.exe secret | ...始终是一种选择,其中其他需要传递ConvertTo-SecureString -AsPlainText的同时也表示PowerShell考虑使用未键入的纯文本输入互动。

[2]自动-Force变量是枚举器(类型),用于按需枚举来自stdin 的行。 不支持这样的索引访问权限,例如$Input;相反,必须使用$Input[0]开始枚举,然后访问.MoveFirst()属性以获取第一行。这样,剩下的元素就不会被消耗,以后使用.Current会产生剩下的行。
$Input周围的$(...)的唯一原因是,为了概念清晰起见,可以将两个语句包装在返回第一行的 single 语句中。先使用$null = $Input.MoveNext(); $Input.Current$null = $Input.MoveNext()也很好。