Signs of Triviality

Opinions, mostly my own, on the importance of being and other things.
[homepage] [index] [] [@jschauma] [RSS]

Passing Passwords

November 4th, 2015

Passwords are such a pain in the ass. No matter how much you like to avoid them, you'll always find yourself in a situation where you have to pass one to a tool in a non-interactive manner (lest you make people write expect scripts that inject a password into anything that prints "assword:" to the terminal). Doing so always carries some risk of exposure, and how to best deal with these tools is, as usual, a question best answered with "it depends". But since this is such a common problem, here are some considerations you may find worthwhile.

Passwords on the command-line

Example use case:

cmd -p password

This is bad for (at least) two reasons:

  • the password itself is leaked into the process table, and thus visible to anybody running ps(1) on the system
  • the password is leaked into your shell's history file

Sometimes people try to be clever and store the password in an environment variable:

export PASSWORD=bacon
cmd -p ${PASSWORD}

This doesn't solve anything. The shell still expands the command prior to invocation and leaks the password into the process table as well as your shell's history, only now the password is also available in the environment.

Another approach might be to store the password in a flat text file (protected by Unix permissions), and to retrieve it via backticks (`command`) or a subshell ($(command)):

cmd -p `cat secret`

This is marginally better than specifying the password on the command-line, as it avoids leaking the password into the shell history. However, this still leaks the secret into the process table, as the shell evaluates the full command-line, finds the backticks, spawns a subshell, runs cat secret, and then replaces the whole backtick part with the result in the process table. To wit:

$ sleep `echo 10` & ps x | grep [s]leep
[1] 24946
24946 pts/3    S      0:00 sleep 10

Now some tools try to be clever and explicitly manipulate **argv when a parameter is used as a password. mysql(1) is known to do this:

$ mysql -u root -p$(cat secret) >/dev/null & ps x | grep mysq[l]
[1] 5554
 5554 pts/0    T      0:00 mysql -u root -px xxxxx

That is, mysql(1), when parsing the command-line arguments, will explicitly overwrite the actual value of the argument passed to -p with 'x'. This leaks at the very least the length of the password into the process table (as shown above), but what people tend to overlook is that the action of manipulating **argv is not (cannot) be atomic at program start-time. That is, there exists a race condition between the command being invoked and mysql(1) changing the value of argv[4] (in this case), and it's conceivable that an attacker might be able to observe the secret prior to it having been overwritten with 'x'.

So no good. Don't pass passwords on the command-line.

Passwords via the environment

Another common method of specifying a password might be by setting an environment variable:

$ SECRET=$(cat secret) cmd

Being a smart little cookie, you shrewdly avoided leaking the secret into the process table and shell history when setting the environment variable, since the shell expands variables prior to command execution; cmd(1) was equally shrewdly written to look for the SECRET environment variable.

So far, so good. Environment variables are not generally available to other (non-root) processes. Except... well, any process with the same EUID might still be able to access e.g., /proc/pid/environ. In addition, there is still a risk of exposure to other EUID processes, as many programs happily dump their environment to stderr under certain (error) conditions, or log it via syslog(3) for debugging purposes. That is, unless you know that the application is carefully written not to do this, you might end up leaking the secret from the environment, even if you took care to prevent it from being leaked in the command invocation.

(This caveat of the application accidentally (or intentionally) logging the secret does of course apply to all cases, but it is notably easier for the application programmer to forget to scrub a specific environment variable than to not log a specific internal variable.)

See also: Why you shouldn't use ENV variables for secret data.

Passwords from a file

As shown above, you may retrieve your password from a file (e.g., cat secret). Some applications can read a configuration file containing a password, and in many cases that is a very reasonable approach: you protect the file itself with the appropriate Unix permissions and allow the program to read it (possibly with elevated privileges).

This approach has drawbacks only in that the secret must live in the file in question in the format the application expects. Often times that means you need to have a configuration file format (json, yaml, ...) that mixes other, non-sensitive options with your password. Which, in turn, may lead to your configuration file being committed to into your revision control system, which may or may not be accessible by other parties. No good.

Another issue here is that the secret must likely exist on disk, which under certain circumstances you'd rather want to avoid. The best solution here is to allow reading a password from a file, but to not insist on a specific syntax. Instead, read the first line of data from the given file. This allows you to prepare or provide the password in a variety of ways:

1.) From stdin or via a pipe:

$ get-secret appid | cmd -p -

Here, we are using the get-secret command to generate the actual secret. This is a stand-in for any key management solution you may be using, including gpg --decrypt somefile, jass -d <somefile, or one of the AWS KMS based tools.

By using the age-old Unix convention of having a command read data from stdin when a "filename" is given as -, you can easily pipe the secret into your tool. Even if the tool was not written to suitably interpret -, you can still use this approach by way of the (now rather common) /dev/stdin device (which may or may not be a symlink to /proc/self/fd/0, for example).

2.) From a fifo:

$ mkfifo -m 0600 /tmp/fifo
$ get-secret appid > /tmp/fifo &
$ cmd -p /tmp/fifo

This allows you to feed the the secret into the tool without leaking it to disk. The bash shell allows you to use process substitution in a similar manner (and may actually implement process substitution using a fifo, although you have the advantage of not having to worry about permissions on the fifo or to remove it after the fact). To wit:

% bash
$ cmd -p <(get-secret appid)

Here, <(get-secret appid) gets replaced with a short-lived fifo under /dev/fd, which cmd can then read from.

Random side-note: apparently on some systems, process substitution does not honor the user's umask, leading to a possible information disclosure vulnerability:

$ umask
$ ls -l <(echo moo)
prw-rw----  0 jschauma users  4 Nov  4 21:50 /dev/fd/63

3.) Using a here-doc or here-string:

For example, if your shell supports here-strings:

$ cmd -p - <<< password

However, note that some shells (such as bash(1)) implement here-docs and here-strings via a temporary file:

$ ls -l /prov/self/fd/0 <<< whatever
lr-x------ 1 jschauma users 64 Mar 10 04:15 /proc/self/fd/0 -> /tmp/sh-thd-1646882992 (deleted)

That is, the shell would create the temporary file, write the password to it, close it, open it again, unlink it, and then dup(1) the filedescriptor onto stdin of the command to be executed.

This, theoretically, opens up a small chance of a race condition. Later versions of bash(1), for example, appear to implement these here-docs and -strings using a pipe, eliminating this risk.

Now while this prevents the password from leaking into the process table, it unfortunately still is recorded in your shell history. That, in turn, can be avoided by using a here-doc instead of a here-string:

$ cmd -p - <<EOF
> password

Best practices

So, with the above in mind, here are my recommendations:

  • Either in ~/.bashrc or, better, system wide, set a restrictive umask
    This helps keep any credentials you might create in the filesystem (actual files or fifos) protected from other users. umask 0077 is your friend.
  • Either in ~/.bashrc or, better, system wide, disable shell history
    This also helps unconsciously leaking credentials that you may have entered on the command-line (for example via export SECRET=bacon or cmd -p bacon, trusting cmd to not leak the password into the process table).
  • If possible, at runtime retrieve the secret from protected storage / a key management service and feed it into the tool without any intermediate files. This requires the tool in question to:
  • Allow passing of the secret from a file.

Got any additional recommendations or concerns about anything I've discussed above? Let me know!

November 4th, 2015


[Three Simple Questions] [Index] [Using the OS X Keychain to store and retrieve passwords]