3

I'm trying to make a username and password program in Python, and although it isn't necessary to the final program, I thought it would be nice to make it display a different character when they input their text. Like most password fields, which may display something like an asterisk (*). So I was wondering how I might go about displaying their input as a different character. Thank you for any help :)

Andrea Corbellini
  • 17,339
  • 3
  • 53
  • 69

1 Answers1

2

Using the getch recipe from the question " Python read a single character from the user "

def getpass(prompt):
    sys.stdout.write(prompt)
    sys.stdout.flush()
    password = ''
    while True:
        c = getch()
        if c == '\r':
            sys.stdout.write('\n')
            sys.stdout.flush()
            return password
        password += c
        sys.stdout.write('*')
        sys.stdout.flush()

Proof:

>>> getpass('secret: ')
secret: ********
'asdfsafd'

Echoing * for each character typed may fell cool from the UI perspective but will compromise security a bit because it gives up the length of the password. I guess that is why the builtin getpass module will not echo anything.

[update]

According to Andrea Corbellini, this implementation will leak keystrokes if the system is under heavy load, because it is resetting the tty before returning - characters may be echoed after getch() has returned and before getch() is called again. Looks like this is very hard to accomplish in a safe way, so probably this is the reason why the builtin getpass is not providing a visual feedback for each keystroke.

Community
  • 1
  • 1
Paulo Scardine
  • 73,447
  • 11
  • 124
  • 153
  • 1
    I don't know about Windows, but on UNIX this solution is subject to race conditions: if you type a character while `if c == '\r' ... password += c ; sys.stdout.write('*') ...` is executed, then the character will be displayed – Andrea Corbellini Feb 04 '16 at 17:50
  • @AndreaCorbellini Are you sure? We are not using asynchronous I/O here, `getch` is blocking. If you are able to trigger this bug, could you be kind enough to post a reproducible case? – Paulo Scardine Feb 04 '16 at 17:58
  • 2
    The problem is not when `getch` is running, but when it's not running. Try putting a `for i in range(100000): pass` after the call to `getch()` (to simulate slow code or high CPU load) and run your script holding `a` pressed. You'll see an alternance of `*` and `a` characters on the terminal – Andrea Corbellini Feb 04 '16 at 18:00
  • The `getch()` implementation you are using is resetting the tty before returning. So characters may be echoed after `getch()` has returned and before `getch()` is called again – Andrea Corbellini Feb 04 '16 at 18:03
  • Thanks. I guess it is hard enough to trigger that the OP can use it in practice if he accepts the risk of echoing some keystrokes when the system is under heavy load. – Paulo Scardine Feb 04 '16 at 18:08