64

I need to read a password from standard input and wanted std::cin not to echo the characters typed by the user...

How can I disable the echo from std::cin?

here is the code that I'm currently using:

string passwd;
cout << "Enter the password: ";
getline( cin, passwd );

I'm looking for a OS agnostic way to do this. Here there are ways to do this in both Windows and *nix.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
Vargas
  • 2,125
  • 2
  • 33
  • 53

4 Answers4

76

@wrang-wrang answer was really good, but did not fulfill my needs, this is what my final code (which was based on this) look like:

#ifdef WIN32
#include <windows.h>
#else
#include <termios.h>
#include <unistd.h>
#endif

void SetStdinEcho(bool enable = true)
{
#ifdef WIN32
    HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); 
    DWORD mode;
    GetConsoleMode(hStdin, &mode);

    if( !enable )
        mode &= ~ENABLE_ECHO_INPUT;
    else
        mode |= ENABLE_ECHO_INPUT;

    SetConsoleMode(hStdin, mode );

#else
    struct termios tty;
    tcgetattr(STDIN_FILENO, &tty);
    if( !enable )
        tty.c_lflag &= ~ECHO;
    else
        tty.c_lflag |= ECHO;

    (void) tcsetattr(STDIN_FILENO, TCSANOW, &tty);
#endif
}

Sample usage:

#include <iostream>
#include <string>

int main()
{
    SetStdinEcho(false);

    std::string password;
    std::cin >> password;

    SetStdinEcho(true);

    std::cout << password << std::endl;

    return 0;
}
Millie Smith
  • 4,536
  • 2
  • 24
  • 60
Vargas
  • 2,125
  • 2
  • 33
  • 53
  • gcc warns here: /home/curtine/git/pub/sxx/src/sxx.cpp: In function ‘void set_stdin_echo(bool)’: /home/curtine/git/pub/sxx/src/sxx.cpp:84:19: error: negative integer implicitly converted to unsigned type [-Werror=sign-conversion] tty.c_lflag &= ~ECHO; – ericcurtin Apr 24 '18 at 10:10
  • Gonna disable that warning I think – ericcurtin Apr 24 '18 at 10:16
  • This is a good answer as far as the not echoing aspect but I have downvoted it because it reads the password into a std::string which is not secure (see e.g. https://stackoverflow.com/questions/5698002/how-does-one-securely-clear-stdstring). Would you consider reworking it to read into a char[]? I'd happily reverse my downvote in that case. – nickform Apr 09 '20 at 07:16
  • Also, in relation to @ericcurtin's comment, I have found `&= ~(0u | ECHO)` a better way of avoiding the warning than simply disabling it. Funny API though. :-$ – nickform Apr 09 '20 at 07:18
  • @nickform char[] is no more secure than a string if you don't rewrite over the data after – ericcurtin Apr 09 '20 at 08:43
  • @ericcurtin agreed but the point is that you *can't* do that reliably with a std::string. I think that an accepted answer for a question about password handling really should exemplify best practice in as many ways as possible. I think it would be entirely reasonable not to cover the clearing of the buffer in the example though. – nickform Apr 09 '20 at 09:23
  • 1
    Just a reminder to everyone reading, StackOverflow is not professional security advice. That said, @nickform, you can easily replace the `std::cin` for a `char buffer[BUF_SIZE]; fgets(buffer, BUF_SIZE, stdin);` but you will need to securely rewrite the buffer, as Eric pointed out, And if you are going to manually overwrite it you can also do it with the `std::string`. The thing you can't do is create a class that will do it automatically for you (deriving from std::string). – Vargas Apr 13 '20 at 14:09
  • @Vargas why an allocator can't be used, and unique_ptr with deallocator that would zero the string object, combining both? – Swift - Friday Pie Nov 12 '20 at 22:11
10

There's nothing in the standard for this.

In unix, you could write some magic bytes depending on the terminal type.

Use getpasswd if it's available.

You can system() /usr/bin/stty -echo to disable echo, and /usr/bin/stty echo to enable it (again, on unix).

This guy explains how to do it without using "stty"; I didn't try it myself.

PlasmaHH
  • 15,673
  • 5
  • 44
  • 57
Jonathan Graehl
  • 9,182
  • 36
  • 40
  • I guess `getpasswd` would really help. But I'm looking for a way to do this without recurring to OS black magic – Vargas Sep 11 '09 at 22:04
  • The getpasswd link is refusing to load, and my machine doesn't have a manual entry for it. Maybe it was removed at some point? The closest equivalent is [getpass](https://man7.org/linux/man-pages/man3/getpass.3.html), but it's deprecated and the manual recommends setting the ECHO flag instead (i.e. [this solution](https://stackoverflow.com/a/1455007/6296561)) – Zoe Jul 17 '23 at 10:58
7

If you don't care about portability, you can use _getch() in VC.

#include <iostream>
#include <string>
#include <conio.h>

int main()
{
    std::string password;
    char ch;
    const char ENTER = 13;

    std::cout << "enter the password: ";

    while((ch = _getch()) != ENTER)
    {
        password += ch;
        std::cout << '*';
    }
}

There is also getwch() for wide characters. My advice is that you use NCurse which is available in *nix systems also.

Khaled Alshaya
  • 94,250
  • 39
  • 176
  • 234
1

Only idea what i have, you could read password char by char, and after it just print backspace ("\b") and maybe '*'.

IProblemFactory
  • 9,551
  • 8
  • 50
  • 66
  • 7
    If someone is logging the terminal output to a file then the whole password will be there. Probably not a good idea, then. – Artelius Sep 11 '09 at 22:14
  • 5
    Same case as someone logging all key pressing :) – IProblemFactory Sep 11 '09 at 22:21
  • 2
    This is insecure, and a bad idea. It's inacceptable to rely on the other side of the pipe for your own security. And be careful not to pipe over the network. And logging output != logging keypress: you will typically give the log file's read userright to more people than those with the rights to write over the terminal emulator program, or to hook into terminal or system events or keyboard driver. – ignis Oct 02 '12 at 07:48