Bash – most secure and simplest way to have a user-typed password on bash become part of stdin to a program

bashcurlpasswordSecurity

I'm looking for the (1) most secure and (2) simplest way to have a user type a password on a bash shell prompt and to have that password become part of stdin to a program.

This is what the stdin needs to look like: {"username":"myname","password":"<my-password>"}, where <my-password> is what was is typed into the shell prompt. If I had control over the the program the stdin, then I could modify it to securely prompt for a password and put it into place, but the downstream is a standard general purpose command.

I have considered and rejected approaches that use the following:

  • the user typing the password into the command line: the password
    would be shown on the screen and would also visible to all users via
    "ps"
  • shell variable interpolation into a argument to an external program (e.g., ...$PASSWORD...): the password would still be visible to all
    users via "ps"
  • environment variables (if they are left in the environment): the password would be visible to all child processes; even trustworthy
    processes might expose the password if they dump core or dump
    environment variables as part of a diagnostic
  • the password sitting in a file for an extended period of time, even a file with tight permissions: the user may accidentally expose the password and the root user might accidentally see the password

I'll put my current solution as an answer below, but will happily select a better answer if someone comes up with one. I'm thinking there should be something simpler or maybe someone sees a security concern that I have missed.

Best Answer

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