Reading a password from std::cin
Asked Answered
C

4

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.

Chanda answered 11/9, 2009 at 21:47 Comment(1)
#13688269Ketchup
C
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;
}
Chanda answered 11/9, 2009 at 21:47 Comment(8)
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;Textualism
Gonna disable that warning I thinkTextualism
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. #5698502). Would you consider reworking it to read into a char[]? I'd happily reverse my downvote in that case.Sciatica
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. :-$Sciatica
@Sciatica char[] is no more secure than a string if you don't rewrite over the data afterTextualism
@Textualism 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.Sciatica
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).Chanda
@Chanda why an allocator can't be used, and unique_ptr with deallocator that would zero the string object, combining both?Unwieldy
M
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.

Majolica answered 11/9, 2009 at 21:50 Comment(2)
I guess getpasswd would really help. But I'm looking for a way to do this without recurring to OS black magicChanda
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, but it's deprecated and the manual recommends setting the ECHO flag instead (i.e. this solution)Cf
A
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.

Animus answered 11/9, 2009 at 22:5 Comment(0)
D
1

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

Diddle answered 11/9, 2009 at 21:57 Comment(3)
If someone is logging the terminal output to a file then the whole password will be there. Probably not a good idea, then.Fathead
Same case as someone logging all key pressing :)Diddle
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.Pilsudski

© 2022 - 2024 — McMap. All rights reserved.