windowsshellpowershellsecuritywindows-scripting

Is it secure to use a password argument in a Windows command?


Imagine that we have a program or script that can take a password (or other sensitive information) argument:

> program.exe /password:secret

For Linux, best practice generally recommends against specifying the password directly on the command-line because of potential security concerns (the password can appear in the shell's history file and the system's process table):

$ ./program.sh --password 'secret' &
[1] 4152

$ cat /proc/4152/cmdline 
/bin/sh./program.sh--passwordsecret

However, when searching around, I don't see the same vigorous recommendation for Windows.

When writing programs and scripts for Windows, should we provide an alternate means to input a password besides an argument to a command-line option to avoid unintentionally exposing the password?

The answer to this question suggests that a dedicated password input prompt improves security by preventing shoulder-surfing. Are there any other ways to exploit a visible password in a command string that justifies writing alternate input methods, both in the context of a shell and in the way Windows manages processes?

Do the security implications change when starting a program from a batch file or PowerShell script that passes a password as an argument to the program?

$password = # prompt for password 
program.exe /password:$password

Edit - I understand that we can misinterpret this question as opinion-based because I mention "best practices" and "recommendations" to provide background. However, I seek specific evidence that demonstrates how a password might be vulnerable in a command argument and—if this assumption is valid—concrete examples or references that depict secure alternatives.


Solution

  • Windows historically didn't save command history between sessions, only within the session. This was true for the command prompt and for PowerShell.

    As Bill Stewart pointed out, Windows PowerShell on Windows 10 and Windows 2016 includes PSReadline by default, which does save your command history between sessions. You can see this by looking at the file here: (Get-PSReadLineOption).HistorySavePath.

    But even if it's set to off, or on an OS version that didn't offer the option, that doesn't mean entering a plaintext password as an argument is a good idea.

    If you must offer that, you should also have a way to have the program prompt at run time.

    For PowerShell and other .Net applications, you have another issue with accepting plaintext passwords: they linger in memory and there's no good way to explicitly clear them.

    This issue is two-fold: strings are immutable in .Net, which means you cannot just modify the string with nulls or random characters to clear it in memory (you will actually be creating a brand new string), and on top of that you cannot control when a specific object will handled by garbage collection, so you can't explicitly remove it.

    This is why the SecureString class exists, but not everything can use this.

    In PowerShell, there is a PSCredential object which stores a user name in plain text and a password as a SecureString. This should always be used in PowerShell, and should be the preferred argument type (in lieu of a separate user name and password).

    Most commands in PowerShell that require a credential take it as this type of object.

    You can retrieve a plaintext version of the password easily with this object as well. Doing so then puts that into a managed string and you get the risks I mentioned above.

    In my opinion though, it is still preferable to use a PSCredential object in these situations, right up until the point you need the plaintext version. It helps to maintain the standardization of this type in both a built-in/'official' capacity, as well as in user-defined commands.

    This type is also easily serializable with Export-Clixml into a form that is encrypted. This can give you a really nice way of providing an automated option to use stored credentials in scripts, with nothing in plaintext, and no prompting or user intervention required.