Skip to content

reading passwords from the console in C#

I’m working on a simple command-line app, and have the need to collect a username and password. I don’t want the password to be printed on the screen as they type, but System.Console.ReadLine doesn’t seem to have any option to mask the input before it’s echoed back to the console.
There are a couple ways I found to resolve this, the easy way, and the harder way with better UI.

Easy way:
[csharp]
ConsoleColor oldFore = Console.ForegroundColor;
Console.ForegroundColor = Console.BackgroundColor;
string password = Console.ReadLine();
Console.ForegroundColor = oldFore;
[/csharp]

This basically hides the echoed input by setting the text color to be the same as the background color. This is a little weird because your users can’t see any indication that they’ve entered any information.

The following function catches the input, and then echoes “*” instead. There’s some rudimentary handling of backspace, but no other special keys (end, delete, arrow keys, etc) are handled properly. For now it’s good enough for my use:
[csharp]
public static string ReadPassword() {
Stack passbits = new Stack();
//keep reading
for (ConsoleKeyInfo cki = Console.ReadKey(true); cki.Key != ConsoleKey.Enter; cki = Console.ReadKey(true)) {
if (cki.Key == ConsoleKey.Backspace) {
//rollback the cursor and write a space so it looks backspaced to the user
Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
Console.Write(” “);
Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
passbits.Pop();
}
else {
Console.Write(“*”);
passbits.Push(cki.KeyChar.ToString());
}
}
string[] pass = passbits.ToArray();
Array.Reverse(pass);
return string.Join(string.Empty, pass);
}
[/csharp]
A bit messy and bug-ridden, but for now it’s good enough for my purposes. When C# 3 comes out, I might be able to add things like this to the Console class using extension methods. Maybe a community-driven FrameworkPlus library will get some momentum, and we can all ditch our home-grown Utils libraries and reap the benefits of each other’s work.

14 Comments

  1. shirlz wrote:

    Hey thanks heaps for that.. works like a charm!

    Monday, September 15, 2008 at 1:47 am | Permalink
  2. Bill wrote:

    I would only make one real suggestion and that is to treat it like Console.IO.ReadLine and thus add this line of code just before your return statement:

    Console.Out.Write(Console.Out.NewLine);

    Wednesday, October 22, 2008 at 6:50 pm | Permalink
  3. Will Buttitta wrote:

    One other adjustment…The block that handles the backspace should be wrapped in a condition that checks whether or not the stack has any password characters (i.e. if (passbits.Count > 0) {…), otherwise an empty stack exception is thrown if the user hits the backspace key one too many times.

    Sunday, December 7, 2008 at 1:37 pm | Permalink
  4. Matt wrote:

    Brilliant starting point; thanks very much. This really helped. The code is simple to follow. I managed to extend to check the cursor position wasn’t already at the start of the line if the Backspace key was pressed and only accept upper/lower case letters or numbers:

    public static string ReadPassword()
    {
    Stack passChars = new Stack();
    // Keep reading user entry:
    for (ConsoleKeyInfo cki = Console.ReadKey(true); cki.Key != ConsoleKey.Enter; cki = Console.ReadKey(true))
    {
    // If Backspace is pressed:
    if (cki.Key == ConsoleKey.Backspace)
    {
    // Check cursor position is not already at 0 (start of line):
    // Rollback the cursor and write a space so it looks backspaced to the user
    if (Console.CursorLeft != 0)
    {
    Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
    Console.Write(” “);
    Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
    passChars.Pop();
    }
    else
    {
    Console.Write(” “);
    Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
    }
    }
    // Only accept keys; 0-9, A-Z and a-z
    else
    {
    if ((cki.KeyChar >= 48 && cki.KeyChar = 65 && cki.KeyChar = 97 && cki.KeyChar <= 122))
    {
    Console.Write(“*”);
    passChars.Push(cki.KeyChar.ToString());
    }
    else
    {
    Console.Write(” “);
    Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
    }
    }
    }
    string[] pass = passChars.ToArray();
    Array.Reverse(pass);
    return string.Join(string.Empty, pass);
    }

    Thursday, April 23, 2009 at 9:33 am | Permalink
  5. tjrobinson wrote:

    That works pretty well, thanks.

    I’ve also found this which looks like it may be a more secure alternative:
    http://derekslager.com/blog/posts/2006/10/reading-masked-input-from-the-system-console.ashx

    Friday, July 31, 2009 at 5:18 am | Permalink
  6. Casey Morton wrote:

    Thanks for the code snippet. I made a couple of improvements on it though. First it would be more efficient to use a queue to store the characters. That way you save a reverse call at the end. Also, on the code posted by matt, it would be better to check the size of the stack/queue than check the position of the cursor. Checking the position of the cursor constrains you to only using this when the cursor is at the beginning of a new line. If you check the size of the stack/queue you can use it to do same-line prompts as well.

    Tuesday, January 26, 2010 at 12:26 pm | Permalink
  7. Casey Morton wrote:

    For anyone interested, here is my improved code:

    public static string MaskedReadLine()
    {
    Queue passbits = new Queue();

    for (ConsoleKeyInfo cki = Console.ReadKey(true); cki.Key != ConsoleKey.Enter; cki = Console.ReadKey(true))
    {
    if (cki.Key == ConsoleKey.Backspace)
    {
    if (passbits.Count > 0)
    {
    Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
    Console.Write(” “);
    Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
    passbits.Dequeue();
    }
    }
    else
    {
    Console.Write(“*”);
    passbits.Enqueue(cki.KeyChar.ToString());
    }
    }
    Console.WriteLine();
    string[] pass = passbits.ToArray();
    return string.Join(string.Empty, pass);
    }

    Tuesday, January 26, 2010 at 12:28 pm | Permalink
  8. Jerry Chen wrote:

    It’s cool, thanks.

    Wednesday, April 7, 2010 at 6:27 am | Permalink
  9. Adam Tappis wrote:

    Queue.ToArray() returns and an object[] so the above example fails. Why not just use a StringBuilder. Here’s my version which hadles both regular and masked input:

    internal static string GetConsoleInput(string prompt, bool hide)
    {

    StringBuilder input = new StringBuilder();

    Console.Write(prompt);

    for (ConsoleKeyInfo keyinfo = Console.ReadKey(true); keyinfo.Key != ConsoleKey.Enter; keyinfo = Console.ReadKey(true))
    {
    //handle backspace
    if (keyinfo.Key == ConsoleKey.Backspace)
    {
    if (input.Length > 0)
    {
    Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
    Console.Write(” “);
    Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
    input.Length -= 1;
    }
    }
    else
    {
    Console.Write(hide ? ‘*’ : keyinfo.KeyChar);
    input.Append(keyinfo.KeyChar);
    }
    }
    Console.WriteLine();

    return input.ToString();

    }

    Friday, September 17, 2010 at 4:52 pm | Permalink
  10. Anurag wrote:

    Thanks, man . Exactly what i wanted, on the first page of the blog.. .

    Wednesday, April 13, 2011 at 11:38 pm | Permalink
  11. Florin C wrote:

    Good job,
    Thanks to all here.

    Sunday, August 7, 2011 at 6:35 pm | Permalink
  12. rnaga wrote:

    There is one another implementation found at

    http://cinchoo.wordpress.com/2012/02/07/cinchoo-read-password-from-console/

    Tuesday, February 7, 2012 at 2:44 pm | Permalink
  13. dan8912 wrote:

    If you need to use the code after a message, just to be sure it won’t delete the initial mesage and get you an error(when try to pop) use the following code:

    public static string ReadPassword() {
    Stack passbits = new Stack();
    //keep reading
    int initpoz = Console.CursorLeft;
    for (ConsoleKeyInfo cki = Console.ReadKey(true); cki.Key != ConsoleKey.Enter; cki = Console.ReadKey(true)) {
    if (cki.Key == ConsoleKey.Backspace && Console.CursorLeft>initpoz)
    {
    //rollback the cursor and write a space so it looks backspaced to the user
    Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
    Console.Write(” “);
    Console.SetCursorPosition(Console.CursorLeft – 1, Console.CursorTop);
    passbits.Pop();
    }
    else {
    if (Console.CursorLeft >= initpoz && cki.Key != ConsoleKey.Backspace)
    {
    Console.Write(“*”);
    passbits.Push(cki.KeyChar.ToString());
    }
    }
    }
    Console.SetCursorPosition(0, Console.CursorTop + 1);
    string[] pass = passbits.ToArray();
    Array.Reverse(pass);
    return string.Join(string.Empty, pass);
    }

    Anyway thaks a lot for code! good job!

    Dan

    Tuesday, April 3, 2012 at 5:01 am | Permalink
  14. fauzia wrote:

    Great! Such a simple/hacky way to hide user input. Highlighting the invisible input doesn’t make the input visible; only copy-paste exposes the input, so not bad! Other sites I scoured shared options that involved writing 20 lines of code (incl System.Security and backspace techniques). Thanks!

    Thursday, February 14, 2013 at 7:14 pm | Permalink