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
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.
.
-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 ?
-AsMaskedPlainText
switch is ON@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:
-Value "********"
etc would still hide the string contents, only stopping when the closing quote is applied (bonus: include here-strings?)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.
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.
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
.