I’m trying to find a solution which can filter user input for a password char-wise via read
command (not by read -e
, which prevents directly responding to key strokes), filtering and handling special characters in real-time. As first char, space
or any special chars
shouldn’t be allowed. How to achieve that, yet avoiding non-printable ESC-sequences
(everything which moves the cursor, apart from backspace
), which potentially would pollute the password input?
# Set of allowed chars
allowedDefaultChars='[a-zA-Z0-9]'
allowedSpecialChars='[-_ ()/~?!§,.#+]'
pw=
while true; do
read -rsn 1
if [[ $REPLY ]]; then
...
pw+="$REPLY"
fi
done
echo "$pw"
Through further investigation and experimentation, I’ve come to the following solution:
#! /bin/bash
# Validated password entry with filtered ESC sequences / disabled ESC and a set of allowed special chars / disallows special chars or space as 1st char
validate_password() {
local pw
local escKey=$'x1B'
local backspaceKey=$'x7f'
local allowedDefaultChars='[a-zA-Z0-9]'
local allowedSpecialChars='[-_ ()/~?!§,.#+]'
echo -e "n*** Password validation ***nAllowed special charset, unless 1st char is >> $allowedSpecialChars <<nnEnter password:" >/dev/tty
local isFirstChar="1"
while true; do
read -rsn 1
# Stop on ENTER
if [[ ${#REPLY} -eq 0 ]]; then
break
fi
# Filter ESC sequences / block ESC key
if [[ $REPLY == $escKey ]]; then
while true; do
read -rsn1 -t 0.001
if [[ ${#REPLY} -eq 0 ]]; then
break
fi
done
continue
fi
# Block 1st char special char or space
if [[ $isFirstChar ]]; then
if [[ $REPLY != $allowedDefaultChars ]]; then
echo -e "First char must not be a special char or space!e[1A" >/dev/tty
continue
fi
# Clear and stay in message line
echo -e "e[Ke[1A" >/dev/tty
unset -v isFirstChar
fi
# Filter out disallowed chars for input
if [[ $REPLY == $allowedDefaultChars || $REPLY == $allowedSpecialChars ]]; then
pw+="$REPLY"
printf '%s' '*' >/dev/tty
# Backspace - delete last entered password char and a '*' from dispay
elif [[ $REPLY == $backspaceKey ]]; then
pw="${pw:0:-1}"
printf '%s' $'e[1De[0K' >/dev/tty
fi
done
echo "$pw"
unset pw
echo -e >/dev/tty
}
echo -e "PW IS: $(validate_password)"
The function blocks any input of special chars or space as 1st char, showing a warning message. Upon entering the 1st valid char, all further chars from either valid set of allowedDefaultChars
or allowedSpecialChars
, are added to the local pw
var, printing the next *
on screen.
All visual output is redirected to >/dev/tty
, so as not to pollute the password, if capturing the function call into a var.
If backspace is pressed, the nexte previously stored char, along with the its *
representation on screen, is removed again.
All directly from keyboard accessible keys generating ESC-sequences
/ non-printable chars, should be successfully suppressed and filtered (ESC, F1-F12, arrow keys, home, end, pgup, pgdn, …), even on upon excessive, rapid multiple keypressing, without ‘leaked’ key sequences.
ENTER
finishes the input.
I’ve also tested copy’n’paste input from KeepassXC
, which seemed to work fine (unless the pasted password contains forbidden chars).