Hide password
Solved
aadilove
Posted messages
31
Status
Member
-
fiddy Posted messages 441 Registration date Status Contributor Last intervention -
fiddy Posted messages 441 Registration date Status Contributor Last intervention -
Hello,
My goal is to mask my password while typing.
To be clearer, when I type my password in the console (UNIX), I would like to see '*' instead of the characters entered.
Thank you in advance...
My goal is to mask my password while typing.
To be clearer, when I type my password in the console (UNIX), I would like to see '*' instead of the characters entered.
Thank you in advance...
4 answers
-
yosh !
I don't know if something like that exists, but you can do:
for each character that is not ENTER: I store it, I clear the screen and I display one more '*'
IF the character is ENTER => I validate my stored characters -
Hi aadilove,
Under Linux/Unix, you need to suspend echoing the typed characters to the screen.
You can do it as indicated here:
https://stackoverflow.com/questions/1196418/getting-a-password-in-c-without-using-getpass-3
If you absolutely want to display asterisks (why?), it's more complicated, and you should probably use ncurses.
Like this:
http://www.cplusplus.com/articles/E6vU7k9E/#CURSES-e2
Ncurses is a library in C, the code presented in this link mixes C with a C++ programming style, but it should give you an idea and it's quite simple to fix. Otherwise, you should find other examples programmed in a more orthodox way in C.
Dal-
-
Hi fiddy,
When allowing the user to input something, it's also nice to let them erase their input :-), and therefore to handle the backspace key so that it deletes the previously entered character (the asterisk), potentially up to the first asterisk, and manage the data accordingly... and also handle the enter key, of course.
So, if we want to properly manage this type of user interface, we need to go a bit further.
fgets used in the first link doesn't allow for this, as it only handles a stream and waits for ENTER (or EOF).
I think getchar could be used, with line buffering disabled (by playing with ICANON) in addition to echo, and send \b as shown in the second link to erase the displayed asterisk.
In short, we can do without ncurses, but it’s not trivial (with ncurses or not, by the way), to manage "asterisks", instead of simply returning nothing.
Dal -
-
No, if only the echo is disabled, the terminal works without echo but with line buffering, and therefore the backspace key is handled (like the enter key) without you having to manage it yourself (as proposed in the code on SO, here https://stackoverflow.com/questions/1196418/getting-a-password-in-c-without-using-getpass-3).
It is only if you want to "see stars" that you need to disable line buffering, also disabling canonical mode, as proposed in my adapted code here: https://forums.commentcamarche.net/forum/affich-27337373-masquer-mot-de-passe#13.
Dal
-
-
Hi aadilove, fiddy,
Here is an example that I was able to test on a FreeBSD machine compiled with gcc, to which I connect with a terminal emulated by Putty, adapted from both the first link posted in my message https://forums.commentcamarche.net/forum/affich-27337373-masquer-mot-de-passe#2 and the second, but without using ncurses.
It should work under Linux as well. However, see the remarks below.
#include <stdio.h> #include <stdlib.h> #include <termios.h> #define ENTER_KEY 10 #define BACKSPACE_KEY 127 int main(void) { struct termios oflags, nflags; char password[11]; char ch = ' '; int n = 0; /* disabling line buffering by disabling * canonical mode, in addition to * disabling echo */ tcgetattr(fileno(stdin), &oflags); nflags = oflags; nflags.c_lflag &= (~ICANON & ~ECHO); nflags.c_lflag |= ECHONL; if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) { perror("tcsetattr"); return EXIT_FAILURE; } printf("password (max len %lu): ", sizeof(password) - 1); do { ch = getchar(); if ( (ch != BACKSPACE_KEY) && (ch != ENTER_KEY) && (n < sizeof(password) - 1) ) { printf("*"); password[n] = ch; password[n+1] = '\0'; n++; } else if ( (ch == BACKSPACE_KEY) && (n > 0) ) { printf("\b \b"); n--; password[n] = '\0'; } } while (ch != ENTER_KEY); printf("You typed: %s\n", password); /* restore terminal */ if (tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) { perror("tcsetattr"); return EXIT_FAILURE; } return 0; }
Disabling line buffering allows you to receive the pressed key using getchar() without needing to press the enter key.
The proposed code above also restricts input beyond the maximum capacity of the password string, handles backspace for erasing the asterisks and enter...
Note that the terminal can be configured to send something other than 127 (Ctrl-?) when backspace is pressed. Thus, my Putty allows me to configure the emulated terminal to send Ctrl-H instead.
Dal-
But then, it's simpler to just cancel the echo on the terminal, to avoid having to display the stars and manage everything that comes with it, and just do as described in the first link posted:
https://stackoverflow.com/questions/1196418/getting-a-password-in-c-without-using-getpass-3
It's also safer, apparently, because a person looking at the screen won't be able to count the stars and deduce the number of characters in the password.
Dal -
it's easier to just cancel the echo on the terminal, so you don't have to display the stars and manage everything that comes with it
In raw mode, backspace displays a character. So this will cause a star to be displayed. But it remains consistent with the input of the character into the character string. If you handle backspace, you will have to do it for both variants. But I admit it will be a bit simpler in the variant without display.
For safety, I agree. There's no contest :-). -
By passing the appropriate parameters to the termios struct, you can finely control the behavior of the console, and set the c_lflag field so that ECHO is removed while maintaining ICANON.
https://pubs.opengroup.org/onlinepubs/7908799/xsh/tcsetattr.html
https://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html
This excerpt from man 3 tcflow is clearer on the matter:
Canonical and noncanonical mode The setting of the ICANON canon flag in c_lflag determines whether the terminal is operating in canonical mode (ICANON set) or noncanonical mode (ICANON unset). By default, ICANON set. In canonical mode: * Input is made available line by line. An input line is available when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at the start of line). Except in the case of EOF, the line delimiter is included in the buffer returned by read(2). * Line editing is enabled (ERASE, KILL; and if the IEXTEN flag is set: WERASE, REPRINT, LNEXT). A read(2) returns at most one line of input; if the read(2) requested fewer bytes than are available in the current line of input, then only as many bytes as requested are read, and the remaining characters will be available for a future read(2). In noncanonical mode input is available immediately (without the user having to type a line-delimiter character), and line editing is disabled.
Thus, by keeping the "canonical mode", backspace, enter, etc., behave normally, even though echo is removed, because the terminal continues to process lines.
Regarding security, it’s more than just using the stty system command (which has the same level of complexity), but it can be subject to workarounds, as shown in my proof of concept here https://forums.commentcamarche.net/forum/affich-27337373-masquer-mot-de-passe#24
Dal
... who finds that reading this topic is becoming complicated.
-
-
Thank you everyone for your suggestions. In the end, I proceeded as follows (without displaying '*'):
printf(" Password: ");
system("stty -echo"); // after this line, every character typed will be hidden
fgets(passe, sizeof(passe), stdin);
system("stty echo"); // we return to normal mode-
Good idea not to display the stars.
However, the use of system("stty -echo"); should be banned as it poses security issues and it's not even posix.
But if these limits don't bother you, there's an even simpler solution with the getpass() function that will do what you want directly ;-).
And just for your information, be careful with fgets(), the '\n' will be stored in your pass string if there's room for it. -
Yes, as Fiddy said, calling an external command is not great.
Here is your code with the correction on the string retrieved by fgets suggested by Fiddy:
$ cat aadilove.c #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char passe[11]; char *p; printf(" Password: "); /* after this line every typed character * should be hidden, unless a user * has substituted the stty command */ system("stty -echo"); fgets(passe, sizeof(passe), stdin); if ((p = strchr(passe, '\n')) != NULL) *p = '\0'; /* switch back to normal mode */ system("stty echo"); printf("\nYou typed %s, but nothing " "was supposed to display upon entry\n", passe); return 0; }
Here is a "normal" execution of this code:
$ gcc -Wall aadilove.c -o aadilove $ ./aadilove Password: You typed toto, but nothing was supposed to display upon entry
And here’s an example of what a user, even a non-privileged one, can do with this code (in this case, with the sh - Bourne Shell, adapt for another shell how environment variables are handled):
$ whereis stty stty: /bin/stty /usr/src/bin/stty $ echo $PATH /sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin:/home/ccm/bin $ pwd /usr/home/ccm/temp/testcommandsub $ PATH=/usr/home/ccm/temp/testcommandsub:$PATH $ export PATH $ echo "echo 'hello'" > stty ; chmod +x stty $ ./aadilove hello Password: toto hello You typed toto, but nothing was supposed to display upon entry
You should at least put the full path of stty ("/bin/stty" in my case) in your system command. This is not practical, because one could imagine that a system places it elsewhere. Then, you are also not safe from this command being replaced by another that performs tricks (that will be more difficult), or potential vulnerabilities of this command.
Dal -
Exactly the security flaw I was thinking of.
You should at least provide the full path of stty
It doesn't change anything ^^. Just 6 more characters to type (not counting the enter key ^^) to bypass the call to /bin/stty. And we can start playing with the PATH again. Although it seems to me that this trick has been fixed in kernels above 2.4. It's better to use the exec() family.
Or even better, your program above :-)
-