Password Masking

I'm writing a program that requires the user to enter a password to gain access to the program. I want the program to mask what the user enters. After searching online i found a piece of code that does that:

1
2
3
4
5
6
7
8
9
string password = "";
char c = ' ';

while(c != 13) //Loop until 'Enter' is pressed
   {
   c = getch();
   password += c;
   cout << "*";
   }


This will get the user's password and mask it, however, it has some flaws.
For example, if the user presses the 'Delete' key or 'Backspace' key. It records it as part of the password and will output an asterisk.

Is there some way to make this loop actually perform a backspace or delete function and not include it as part of the password?
You will need to check for, and handle the delete/backspace keys manually.

Much the same way you check for a 'Enter' key.
Ok but how do i go about that? How do i remove a star and tell it to remove the last key press from the buffer?
This forum does have a very good search feature, which no one seems to like to use.
http://www.cplusplus.com/forum/general/3570/page1.html#msg15410
This hasn't even scrolled off the first page yet...
I did use the search and that thread does not answer my question. I know how to output "*" to the screen while still recieving the user's input as the code above shows. What i don't know how to do is accept a backspace or a space and recreate their effects on screen. How do i get rid of an asterisk? how do i not accept a space, etc.
That post provides an explicit example of how to react to specific inputs. Look again.

Line 24 is the same as for (c = getch(); (c != '\n') && (c != '\r'); c = getch())

Line 26 handles the backspace. Add your own checks for other special characters (such as ignoring spaces).

Sorry I seemed grumpy, but the std::string class has all kinds of useful methods for modifying the string.
Ok, I see what you're referring to.
I'm assuming "WriteConsolA" performs the visual aspect of removing the asterisk. I missed that previously because that code is a little beyond my level. I don't quite understand where it came from.
Is there a simpler way to make a character disappear from the screen without having to reload and redisplay everything else currently on the screen?
Hmm, sorry about that.

Most terminals (including the Win32 Console) accept the backspace character (ASCII 8, or '\b') as "move cursor left one space, if possible". But not all terminals actually erase the character to the left. So to erase it and put the cursor where it belongs, I print the string:
"\b \b"
which is: left one, write a space, left one. (I actually just don't remember if the Win32 Console erases or not.)

You can do the same thing with putch() (if I remember the conio.h routine name correctly).
1
2
3
putch( '\b' );
putch( ' ' );
putch( '\b' );


So it doesn't change anything on the screen except the cursor position and one character.

Hope this helps.
Last edited on
Thats the answer i was looking for. Thank you!
I only have one more issue to solve with this and that is specific keys. For example, keys such as the arrows and f1-f12 don't have ASCII values associated with them...so even though the code says only to accept 1-9, a-z, and A-Z, those keys don't apply to those rules apparently cause they still pass and become part of the typed password. Is there any way to get around that?
Conio will have getch() return zero to indicate extended keys. The next character getch()ed will be a special key code.

I had a hard time googling this, so here it is real quick:

Extended Key Codes for use with getch() (C) or readkey (Turbo Pascal):

3 NUL
15 Shift Tab
16-25 Alt-Q/W/E/R/T/Y/U/I/O/P
30-38 Alt-A/S/D/F/G/H/I/J/K/L
44-50 Alt-Z/X/C/V/B/N/M
59-68 F1-F10 (disabled as softkeys)
71 Home
72 Up Arrow
73 PgUp
75 Left Arrow
77 Right Arrow
79 End
80 Down Arrow
81 PgDn
82 Ins
83 Del
84-93 F11-F20 (Shift-F1 to Shift-F10)
94-103 F21-F30 (Ctrl-F1 to Ctrl-F10)
104-113 F31-F40 (Alt-F1 to Alt-F10)
114 Ctrl-PrtScr
115 Ctrl-Left Arrow
116 Ctrl-Right Arrow
117 Ctrl-End
118 Ctrl-PgDn
119 Ctrl-Home
120-131 Alt-1/2/3/4/5/6/7/8/9/0/-/=
132 Ctrl-PgUp
133 F11
134 F12
135 Shift-F11
136 Shift-F12
137 Ctrl-F11
138 Ctrl-F12
139 Alt-F11
140 Alt-F12

So, to read, say, the arrow keys:
1
2
3
4
5
6
7
8
9
10
11
12
  switch (getch())
    {
    ...
    case 0:
      switch (getch())
        {
        case 72: move_up(); break;
        case 75: move_left(); break;
        case 77: move_right(); break;
        case 80: move_down(); break;
        }
    }

Hope this helps.
Once again, thank you! You've been extremely helpful so far and I appreciate it. But unless i'm missunderstanding how getch() actually works or possibly the setup of the above code...this still isn't working. Here's what I have:

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
while(c != '\r') //Loop until 'Enter' is pressed
         {
         c = getch();
         if(c == '0')
            {
            switch(getch())
               {
               default:
                  break;            
               };
            }
         else if(c == '\b')   //If the 'Backspace' key is pressed
            {
            if(password.size() != 0)  //If the password string contains data, erase last character
               {
               cout << "\b \b";
               password.erase(password.size() - 1, 1);
               }
            continue;
            }
         else if(c <= '9' && c >= '0' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')  //If user enters 1-9, a-z, or A-Z, add it to the password and display an asterisk
            {
            password += c;
            cout << "*";
            }
         else
            continue;
         }


It still allows all of the special characters to make it through. I've sat here and played around with the code for quite a while now and i've gotten nowhere it seems.
Last edited on
Line 4 should read if (c == 0) ;-)

You can shorten things considerably using the stuff in <cctype>.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
do
  {
  int c = getch();
  switch (c)
    {
    case 0:
      getch();
      break;
    case '\b':
      if (password.length())
        {
        cout << "\b \b";
        password.erase( password.end() -1 );
        }
      break;
    default:
      if (isalnum( c ) || ispunct( c ))
        {
        cout << "*";
        password.push_back( c );
        }
    }
  }
while (c != '\r');
Using switch statements did condense things a bit as well as some other functions, but even written similar to the code above it still outputs an * when the delete and arrow keys and such are pressed. For some reason those special characters still aren't being filtered.

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
do   //Loop until 'Enter' is pressed
         {
         c = getch();
         switch(c)
            {
            case 0:
               {
               getch();
               break;
               }
            case '\b':
               {
               if(password.size() != 0)  //If the password string contains data, erase last character
                  {
                  cout << "\b \b";
                  password.erase(password.size() - 1, 1);
                  }
               break;       
               }   
            default:
               {
               if(isalnum(c) || ispunct(c))
                  {
                  password += c;
                  cout << "*";           
                  }
               break;     
               }      
            };
         }
      while(c != '\r');
Last edited on
If you are still getting errors then it is because of the age of the <conio.h> library. It was designed to work with 16-bit DOS, not Win32. The Win32 DOS emulator is not particularly good at things like this.

You might want to check out the Borland-style Conio project at SourceForge
http://sourceforge.net/projects/conio/

Good luck!
Well, I downloaded and installed it and it didn't help : ( I'm gonna see if there is some other alternative to the getch() function all together that may handle things differently. There's got to be a way around those special characters
I really don't know why line 22 doesn't filter out unwanted keys. Did you try printing the resulting password to see what the Delete, etc. keys are becoming?

If you are willing to ditch the conio stuff, then I heartily recommend using NCurses, or the Windows port PDCurses.
http://pdcurses.sourceforge.net/

For MinGW you can get a pre-compiled, unzip-and-use package from
http://sourceforge.net/project/showfiles.php?group_id=2435

It isn't designed to work with the standard I/O streams, but that is a small thing to overcome.

Otherwise you'll have to go straight to the Windows API.
I did check to see what those special keys look like and essentially, it prints out some character that looks like the greek letter alpha and then it prints out an uppercase letter.

I'll switch to something other than conio as a last resort...i'd really really really like to get this working...lol

I really appreciate all the help you've given, Thank you.
Topic archived. No new replies allowed.