Powershell: Can we get a MaskInput parameter on Read-Host?

Created on 19 Oct 2019  路  13Comments  路  Source: PowerShell/PowerShell

Summary of the new feature/enhancement

In order to avoid secrets getting stored in my PSReadLine history, I never paste a secret directly onto the command line as a parameter value. I always use Read-Host -AsSecureString and then paste the secret at the Read-Host prompt. The secret input is masked with * chars. Then I run through this little dance to extract the plain text secret into a variable which honestly, is getting a little tiresome to do (and explain to folks why they need to do this):

PS> $ss = Read-Host -AsSecureString
*******************************
PS> $apiKey = [pscredential]::new('jpgr', $ss).GetNetworkCredential().Password

Then I use the $apiKey variable as the parameter value. Now, yeah, it would be better to use a SecureString but unfortunately, I have to use commands that don't accept a secure string and on top of that, SecureString isn't really secure on any platform except Windows.

What would make life easier in this scenario is this simple addition to Read-Host:

$apiKey = Read-Host -MaskInput

Read-Host already knows how to mask input since it does this when you specify -AsSecureString

Proposed technical implementation details (optional)

Add a new parameter set to Read-Host that adds -MaskInput and is mutually exclusive with -AsSecureString and outputs a System.String object. Now, if you want to require this new parameter to require a -Force parameter to work ala ConvertTo-SecureString 'foo' -AsPlainText -Force, that would be OK. And without the -Force parameter, this would error with a similar error message - The system cannot protect plain text input. To suppress this warning and process the plain text secret, reissue the command specifying the Force parameter..

Area-Cmdlets-Utility Hacktoberfest Issue-Enhancement Resolution-Fixed Up-for-Grabs

Most helpful comment

Yup, the idea is to return a plain text string but mask the input while typing/pasting. I also agree with @mklement0 that using As*PlainText is not called for here since the switch does not change the output type. The switch is literally only masking the input hence the suggestion -MaskInput.

All 13 comments

-AsMaskedPlainText?

-As* parameters specify the _output type_, so I don't think -AsMaskedPlainText is appropriate - we're just modifying the UI experience, we're not changing the output type - though I do see the appeal of the symmetry with -AsSecureString (in the latter case we _do_ change the output type, with a modified UI experience being _implied_).

I think something like -Masked is sufficient (plain-text output is the default anyway, no need to mention it) - though perhaps we could implement -AsMaskedPlainText as an alias.

@mklement0 is the expected implementation like below ?

  1. If -AsMaskedPlainText switch is ON
  2. Read as securestring
  3. Convert it to plain text and print

@kvprasoon I think the intent is to skip SecureString completely

@kvprasoon: I haven't looked at the source code; as Steve says, SecureString doesn't need to be in the mix at all - perhaps doing what you suggested is an option for _implementation convenience_, but I suspect that it wouldn't be hard to selectively invoke the masked-input prompt functionality, separate from SecureString.

Yup, the idea is to return a plain text string but mask the input while typing/pasting. I also agree with @mklement0 that using As*PlainText is not called for here since the switch does not change the output type. The switch is literally only masking the input hence the suggestion -MaskInput.

Yup, the idea is to return a plain text string but mask the input while typing/pasting. I also agree with @mklement0 that using As*PlainText is not called for here since the switch does not change the output type. The switch is literally only masking the input hence the suggestion -MaskInput.

Keep in mind that currently AsSecureString is the only parameter that starts with A, so
Read-host -a is a valid way to ask for a secure string. "AsPlaintext" would break this, but "MaskInput" would not (and will allow Read-host -m)

@kvprasoon are you working on it? If not I would like to take a stab at for Hacktoberfest.

hey @davinci26 , yeah I was trying to figure out how to do it (novice in c#). But you can take it and happy Hactoberfest :-)

I mentioned to @TheJasonHelmick at Ignite re: the Secrets module inputs needing a MaskInput sort of idea. Been thinking about that recently and I think we could potentially create a more detached solution than _just_ putting it on Read-Host or New-Secret.

What do y'all think of generalizing this interface into a parameter attribute (e.g., [MaskInput()]) that PS can read and apply when prompting for input, and possibly even _on the actual command line_?

For example, let's say New-Secret has a -Value parameter with [MaskInput()] attribute applied:

PS> New-Secret -Name MySecret -Value ************

That is, PS automatically censors any value for that parameter. It would handle:

  1. bare strings (and stop on space, since that's where the parameter ends)
  2. contents of strings, so -Value "********" etc would still hide the string contents, only stopping when the closing quote is applied (bonus: include here-strings?)
  3. _not_ variable names, I would think; that seems counter-productive

It would also have to remove the data from any logging and history data, including that captured by PSReadLine, or somehow prevent those tools capturing it at all in the first place.

This would let us simply tag parameters we want to be "secret" or masked, at least, and we can then implement this in a wider scope than just read-host, especially as [securestring] is fundamentally not secure in .NET Core.

Read-Host itself is probably fine with a switch, but other cmdlets may want to take values on the command line directly with the ability to mask them. (In scripts this probably wouldn't work, but you're probably doing it wrong anyway if you're trying to hide secrets in a plaintext script!)

@vexx32 . The original problem was X needs a secret but only takes plain text (bad X !). If users must type it, to prevent it being appearing on screen, either the author must specify a parameter type of [securestring()] or call read-host with an -asSecureString.
In either case it's necessary to jump through a hoop to get back to the insecure string.

Personally I think it gives an even greater false sense of security to have parameter input , psreadline etc detecting a parameter attribute and masking things out on the command line. Especially as now the function can be written in a script as a
new-secret -name mySecret -value "Pr0mi5e!2tell"

TBH I'd avoid readhost and just have a securestring parameter ...

The problem with securestring is that it's not... really secure? And you also can't create a securestring from the command prompt or in a script, or in any way really that doesn't either include some kind of prompt, or leave the "secret" value in a log somewhere, which is far from ideal, unless you use Get-Credential to do it -- which is also just another prompt, too; there's no way to create a securestring that doesn't leave a log without a prompt, currently, which feels like an oversight / missing feature. 馃檪

Otherwise... yes, agreed. Perhaps [securestring] parameters could be the basis of masking input, but there's still work needed in PS itself to make that work... and I'm uneasy about using [securestring] as-is, since it's simply not secure. 馃槙

Perhaps we'd need to have a powershell-native type that does a bit of extra work to ensure it's as secure as possible, or something. 馃

@vexx32 well, exactly, commands which turn "Here_is_my_Secret" from clear text into a secure string should never be used.

Dealing with creds my code typically goes.

  1. Does "cred file"exist. If so use import-clixml and get a secure string / credential object.
  2. If it doesn't or reading it fails (wrong user for the _that_ secure string) prompt with Get-credential / read-host -asSecureString ONCE. Save to the cred file with export-clixml.

Two guiding principals I have.
a. Every re-typed password is a manifestation of a design failure
b. The correct PowerShell name for a command which accepts a _secret_ as plain text is Kill-Puppy.

Was this page helpful?
0 / 5 - 0 ratings