------------------------------------------------------------------------------- A Password Reading Helper Script using BASH - Ask Pass Stars This started from discussion on "password scripts" http://www.linuxquestions.org/questions/showthread.php?p=4484096 It is very easy just to read passwords direct from a TTY with echo turned off. But this means you don't get any feedback that what you are typing is actually being seen by the computer. Essentually you end up just typing to a unresponsive prompt until you press return. The better way is to have the 'password input program' (a 'password helper'), type a "star" (or some other characters), whenever you press a key, as feedback that the system is still responding. There is some conserns with doin this which are looked at, at the very end of this document. ------------------------------------------------------------------------------- The following explains the evolution of a password helper script to read passwords from a TTY while echoing only stars (or some other character). That script has now evolved into a much more complex script that also provides a lot of other features, including fall back to "systemd-ask-password" and the caching of passwords in the kernel keyring. You can download the latest version of the script at... https://antofthy.gitlab.io/software/#askpass_stars Anthony Thyssen, 2021 =============================================================================== Version 1 - Basic first attempt for password reading with 'star' feedback =======8<-------- #!/bin/bash unset PASSWORD ANYKEY=0 echo -n "Password: " until [ -z "$ANYKEY" ] do read -r -s -N 1 ANYKEY echo -n "*" PASSWORD="$PASSWORD$ANYKEY" done echo echo $PASSWORD exit =======8<-------- This works but does not handle anything special, like backspaces, or other control characters. ------------------------------------------------------------------------------ Version 2 - Read each character one at a time... =======8<-------- #!/bin/bash unset password # ensure that password is NOT an environment variable prompt="Enter Password : " # Note read exits 'false' on EOF, or some signal (timeout or pipe) while IFS= read -p "$prompt" -r -n1 -s char do # the read $char is empty if a NEWLINE or NULL has been recieved (EOL) [[ "$char" == '' ]] && break prompt='*' password+="$char" done echo >&2 echo "Password=$password" echo "Password length = $(echo -n "$password" | wc -c)" exit =======8<-------- Problem: if user types too fast then the 'silent' (-s) flag on the bash password read will fail, and character gets echoed! Probably still better to have a more global stty noecho around the main loop, but then you have to handle interupts and failures too (see next). Note how the "read -p $prompt" variable is modified to control the user prompting and feedback. Setting the $IFS is needed to prevent white space from delimiting the sequence. But if you use the option -N instead of -n, then a return will not mark the end of the sequence. The alternative is to specify -d$'\r' as the delimeter (replacement for the IFS usage) However the above still does not handle normal terminal control characters such as backspace! It will return those characters as if they were part of the password sequence. Yes you could use such characters in a password, but having any sort of control character in most password readers just not a good idea. If you plan to use control characters in passwords, you may as will be giving your password in hex or base64, to make it more manageable. ------------------------------------------------------------------------------ Version 3 - output stars and handle backspaces and kill line =======8<-------- #!/bin/bash # # Read and echo a password, echoing responsive 'stars' for input characters # Also handles: backspaces, deleted and ^U (kill-line) control-chars # unset PWORD PWORD= echo -n 'password: ' 1>&2 while true; do IFS= read -r -N1 -s char # Note a NULL will return a empty string # Convert users key press to hexadecimal character code code=$(printf '%02x' "'$char") # EOL (empty char) -> 00 case "$code" in ''|0a|0d) break ;; # Exit EOF, Linefeed or Return 08|7f) # backspace or delete if [ -n "$PWORD" ]; then PWORD="$( echo "$PWORD" | sed 's/.$//' )" echo -n $'\b \b' 1>&2 fi ;; 15) # ^U or kill line echo -n "$PWORD" | sed 's/./\cH \cH/g' >&2 PWORD='' ;; [01]?) ;; # Ignore ALL other control characters *) PWORD="$PWORD$char" echo -n '*' 1>&2 ;; esac done echo echo $PWORD =======8<-------- This script outputs one star everytime the user types a character, but also handles both backspace or delete, as well as CTRL-C (interupt) CTRL-U (kill line) and a number of other conditions. Ideally the current 'kill line' and 'interupt' keys, should have been extracted from the "stty", in case the user changed them, (why should they). Other control or meta-characters should also be ignored, but currently aren't. Perhap full ansi-escape sequences could be removed, or acted on (left-right cursour) This is a bare minimum version of the script, but still needs interupt handling. It also still has the echo fast input problem. ------ A more highly evolved version of this password input script can be found at... https://antofthy.gitlab.io/software/#askpass_stars This newer version will also make use of use "systemd-ask-password" instead, if it is available, and also allows use of kernel password caching, in a way that is suitable for file editing (which the systemd version does not handle). ASIDE: On my system the 'DELETE' key sends the ANSI escape sequence "\e[33~" rather than the expected 7F hex character code! This I can understand for fancy function keys, but why do this for 'DELETE' which has a dedicated character! ------- SECURITY NOTE... Using echoed stars... Note printing stars can be a security problem for people looking over someones shoulder can now see just how many characters the user has typed. Ways you can avoid this include... * Output a random number of stars with each character input. But you may need to keep track of the number for 'deletes'. * Show a ascii-art animation (a random number of steps) for each key... + Cycle a spinning line, \ | / - or pulsing star . + * + . + or a short bar with a star bounce back and forth This does not need to keep track of deletions as you just continue the animation when you get a delete or reset line signal. See https://antofthy.gitlab.io/info/ascii/Spinners.txt * Turn on no-echo if user presses 'DELETE' or 'TAB' as the first character. "systemd-ask-password" actually does this, printing "(no echo)". However disconnecting the number of stars from the number of key presses can also be bad, as the user may have hit two keys by accident, or pressed a function key that inputs a multi-character ANSI escape of 4 to 6 characters. In such cases you don't get good feedback when this happens. A 'DELETE' key that returns a ANSI escape sequence, rather than delete a star, is a case in point! I also find when encrypting files, where you want to input two identical passwords twice in a row, that comparing the lengths of the stars to be extremely useful. In fact I make the effort to enure the two prompts (in my script) are also the same length to make this matching easier. For example... Encryption Password : **************** Encryption Pwd Again: **************** I have yet to see any password helper (even GUI) that provides a good double input verification in a single program call. For security the script should erase all stars and replace them with a standard response such as "==password==" at the end of password input. Note: "systemd-ask-password", or my own "askpass_stars" script (above), does neither, though my script does have commented out code for trialing star erase after input, but I never liked it, so it is not enabled. -------------------------------------------------------------------------------