Securely Reading Passwords from the Console
If you've ever written a console application which requires the user to enter sensitive information like a password or a token, you might have wrestled with concerns of exposing the password in plain text within the console window.
I was writing a new console application earlier today after spending most of my time in PowerShell for the last three years, and I found myself wanting to use
Read-Host -AsSecureString, and remembered how much I take for granted that PowerShell gives us so much for free.
After making sure none of the
Console.Read* methods baked into .NET would give me what I wanted, I wrote a fairly short
SecureConsole class with a
SecureConsole.ReadLine() method along with a
SecureConsole.GetCredential(string message) method. I wanted to emulate PowerShell's
Get-Credential since I needed both a username and password.
Here's what I ended up with. The
SecureConsole.ReadLine() method will...
- read any non-control character entered by the user
- append each new
- write an asterisk (*) symbol back to the console
- accept the backspace key and behave as expected
SecureConsole class, and a demo program where I'm calling
SecureConsole.GetCredential() to prompt the user for their credentials. The password will be recorded as a
SecureString and then paired with the username to create a
System.Net.NetworkCredential. For testing purposes, the plain text password from the credential is printed out to verify the text was received properly. Read on after the code sample for details.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
The main loop
The program is a simple loop where we read from the console until the user enters a credential with a blank user name. Once a credential is entered, the password is printed to the screen and we do it again.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
- This loop will continue forever, or until we reach the
break;on line 17 by pressing enter without entering a username.
GetCredential()method is called here without a message, and it returns a
- This is a just a sample, and for testing purposes we print the
Passwordproperty of the network credential we received. This statement uses string interpolation.
- We call
Console.ReadKey()with the boolean
trueto indicate that the key should be suppressed from the console.
The GetCredential() methods
At the top of the
SecureConsole class are the
GetCredential() and an overload
GetCredential(message) which optionally prints the specified message to the user before presenting the "Username" and "Password" fields.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
- In the first overload of the
GetCredential()method, we call the second overload with an empty message.
- In the second overload of
GetCredential(), we print the message to the console if one was provided.
- Then we collect the plain text username and a
System.Security.SecureStringpassword before returning the pair in a new
SecureConsole.ReadLine() method. It's similar to
Console.ReadLine() in behavior as accepts console input until a carriage return is received. The difference is that the character will not be written to the terminal, it gets stored one character at a time into an encrypted
SecureString, and an asterisk will be written to the console so the user recognizes that the character has been received.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
Console.ReadKey(true)to receive a keypress from the console, and as long as it isn't an Enter key, the loop executes.
- Next we check to see if the key was the
ConsoleKey.Backspace. If so, we write two
\bbackspace characters to the terminal on either side of a "space" character. This effectively types "backspace - space - backspace" into the console to erase the last asterisk.
- If the key wasn't a backspace, and the key is also not a control character like CTRL or HOME, then an asterisk is written to the console, and we append the character to the SecureString defined on line 54.
- We're now out of the
whileloop, but the last Enter keypress was suppressed from the console, so we use
Console.Write(Environment.NewLine)to move the console cursor to the start of the next line before returning the completed
There are probably more secure and complex ways to protect user input in a console app and thwart shoulder-surfing ne'er-do-wells, but this method seemed like a solid, lightweight alternative to showing passwords in plain text and storing them in simple strings. I wonder if there's a way to do it where we don't keep an unprotected
char in memory? Let me know if there's a simpler, and/or more secure method to accomplish the same thing within the scope of a console application!