What is most secure and simplest way to have a user-typed password on bash become part of stdin to a program?
With bash
or zsh
:
unset -v password # make sure it's not exported
set +o allexport # make sure variables are not automatically exported
IFS= read -rs password < /dev/tty &&
printf '{"username":"myname","password":"%s"}\n' "$password" | cmd
Without IFS=
, read
would strip leading and trailing blanks from the password you type.
Without -r
, it would process backslashes as a quoting character.
You want to make sure you only ever read from the terminal.
echo
can't be used reliably. In bash
and zsh
, printf
is builtin so that command line wouldn't show in the output of ps
.
In bash
, you need to quote $password
as otherwise the split+glob operator is applied to it.
That's still wrong though as you'd need to encode that string as JSON. For instance, double-quote and backslash at least would be a problem. You probably need to worry about the encoding of those characters. Does your program expect UTF-8 strings? What does your terminal send?
To add a prompt string, with zsh
:
IFS= read -rs 'password?Please enter a password: '
With bash
:
IFS= read -rsp 'Please enter a password: ' password
Here's the solution I have (it's been tested):
$ read -s PASSWORD
<user types password>
$ echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"'
Explanation:
read -s
reads a line of stdin (the password) without echoing the line to the screen. It stores is in the shell variable PASSWORD.echo -n $PASSWORD
puts the password on stdout without any newline. (echo is a shell built-in command so no new process is created so (AFAIK) the password as an argument to echo will not be shown on ps.)- perl is invoked and reads the password from stdin to
$_
- perl puts the password in
$_
into the full text and prints it to stdout
If this is going to a HTTPS POST, the second line would be something like:
echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"' | curl -H "Content-Type: application/json" -d@- -X POST <url-to-post-to>
If your version of read
doesn't support -s
you try the POSIX compatible way seen in this answer.
echo -n "USERNAME: "; read uname
echo -n "PASSWORD: "; set +a; stty -echo; read passwd; command <<<"$passwd"; set -a; stty echo; echo
passwd= # get rid of passwd possibly only necessary if running outside of a script
The set +a
is supposed to prevent auto "export" of a variable to the environment. You should check out the man pages for stty
, there are lots of options available. The <<<"$passwd"
is quoted because good passwords can have spaces. The last echo
after enabling the stty echo
is to have the next command/output start on a new line.