58

Suppose that I were using sha1pass to generate a hash of some sensitive password on the command line. I can use sha1pass mysecret to generate a hash of mysecret but this has the disadvantage that mysecret is now in the bash history. Is there a way to accomplish the end goal of this command while avoiding revealing mysecret in plain text, perhaps by using a passwd-style prompt?

I'm also interested in a generalized way to do this for passing sensitive data to any command. The method would change when the sensitive data is passed as an argument (such as in sha1pass) or on STDIN to some command.

Is there a way to accomplish this?


Edit: This question attracted a lot of attention and there have been several good answers offered below. A summary is:

  • As per @Kusalananda's answer, ideally one would never have to give a password or secret as a command-line argument to a utility. This is vulnerable in several ways as described by him, and one should use a better-designed utility that is capable of taking the secret input on STDIN
  • @vfbsilva's answer describes how to prevent things from being stored in bash history
  • @Jonathan's answer describes a perfectly good method for accomplishing this as long as the program can take its secret data on STDIN. As such, I've decided to accept this answer. sha1pass in my OP was just an example, but the discussion has established that better tools exist that do take data on STDIN.
  • as @R.. notes in his answer, use of command expansion on a variable is not safe.

So, in summary, I've accepted @Jonathan's answer since it's the best solution given that you have a well-designed and well-behaved program to work with. Though passing a password or secret as a command-line argument is fundamentally unsafe, the other answers provide ways of mitigating the simple security concerns.

10
  • 6
    Not only that: Anybody on the same machine with permission to list running processes can potentially see that sha1pass mysecret is running, and hence know what mysecret is. (This only works for the few seconds while the program is actually running, of course...) Commented Apr 23, 2018 at 15:35
  • @MathematicalOrchid This could be avoided by running in a private virtual machine. But that may be too much work to set up to generate only a single password... :-)
    – Kusalananda
    Commented Apr 23, 2018 at 15:41
  • 5
    @Kusalananda My point was more "don't ever put sensitive data on the command line, even if you figure out how to turn off command history"... Commented Apr 23, 2018 at 16:14
  • 2
    Just so you know, SHA-1 is deprecated for key or password hashes since quite a few years now. Commented Apr 23, 2018 at 21:20
  • 2
    Note that if the system is running an audit daemon, all commands and all arguments by all users are logged centrally by root, so anyone who can access those logs will see this.
    – Randall
    Commented Apr 24, 2018 at 9:57

8 Answers 8

47

Ideally, you never type a clear-text password on the command line as an argument to a command. Doing so makes the password an argument to the command, and command line arguments may be seen in the process table by using simple tools like ps or logged into some audit logs.

Having said that, there are certainly ways of hiding the actual password from the shell's command history.

sha1pass "$( head -n 1 )"

Then type in the password and press Enter. The head command used here accept exactly one line of input and the last newline that you type will not be part of the data that is passed to sha1pass.

To prevent the characters from echoing:

sha1pass "$( stty -echo; head -n 1; stty echo )"

The stty -echo command turns off echoing of the typed characters on the terminal. The echoing is then restored with stty echo.

To pass on standard input, that last command could be altered (you would have done this if sha1pass accepted data on standard input, but appears as if this particular utility is ignoring its standard input):

{ stty -echo; head -n 1; stty echo; } | somecommand

If you need multi-line input (the above assumes a single line should be passed, with no newline character at the end), then replace the whole head command with cat and terminate the input (assuming somecommand itself reads until end-of-file) with Ctrl+D (following Return if you want to include a newline character in the input, or twice if not).

This would work regardless of what shell you were using (as long as it was a Bourne-like or rc-like shell).

Some shells may be made to not save the typed-in commands in their history files if the command is preceded by a space. This usually involves having to set HISTCONTROL to the value ignorespace. This is supported by at least bash and ksh on OpenBSD, but not by e.g. ksh93 or dash. zsh users may use the histignorespace option or their HISTORY_IGNORE variable to define a pattern to ignore.

In shells that support reading with read without echoing characters to the terminal, you may also use

IFS= read -rs password     # -s turns off echoing in bash or zsh
                           # -r for reading backslashes as-is,
                           # IFS= to preserve leading and trailing blanks
sha1pass "$password"

but this obviously still has the same issue with potentially revealing the password in the process table.

If the utility reads from standard input, and if the shell supports "here-strings", the above could be changed to

IFS= read -rs password
somecommand <<<"$password"

Summary of comments below:

  • Executing a command with a password given on the command line, which all of the commands above does, except the one that pipes the data to the command, will potentially make the password visible to anyone running ps at the same time. None of the commands above will however save the typed-in password in the shell's history file if executed from an interactive shell.

  • Well behaved programs that reads clear-text passwords do so by reading from their standard input, from a file, or directly from the terminal.

  • sha1pass does requires the password on the command line, either typed in directly or using some form of command substitution.

  • If at all possible, use another tool.

15
  • 11
    This is incorrect. The result of command expansion $(...) will be part of the command line and visible in ps output except on a system with hardened /proc. Commented Apr 23, 2018 at 15:42
  • 3
    @Kusalananda, even overwriting the argv after you started leaves a vulnerable window before that overwrite takes place; it's a mitigation, not a fix. Commented Apr 23, 2018 at 15:45
  • 5
    @Kusalananda, no well-designed program for hashing passwords will require input on the command line, so that conditional is basically a given -- if it's not, one should choose a different tool. Commented Apr 23, 2018 at 15:54
  • 4
    @CharlesDuffy: The tool here seems fundamentally broken. sha1pass run with no password on the command line seems to produce an output without reading anything... So OP needs to choose a different tool. Commented Apr 23, 2018 at 15:59
  • 8
    This answer is unsafe from a security perspective, and the only advantage is that the password doesn't show up in the shell history, but that advantage is easier to accomplish by including a space in front of the command
    – Ferrybig
    Commented Apr 23, 2018 at 17:06
27

If using the zsh or bash shell, use the -s option to the read shell builtin to read a line from the terminal device without it echoing it.

IFS= read -rs VARIABLE < /dev/tty

Then you can use some fancy redirection to use the variable as stdin.

sha1pass <<<"$VARIABLE"

If anyone runs ps, all they'll see is "sha1pass".

That assumes that sha1pass reads the password from stdin (on one line, ignoring the line delimiter) when not given any argument.

6
  • I believe that we established that sha1pass does not use its standard input stream.
    – Kusalananda
    Commented Apr 23, 2018 at 19:47
  • @Kusalananda Okay, but this method will work for programs that do read from stdin.
    – Jonathan
    Commented Apr 24, 2018 at 17:25
  • So, assuming the utility can take its secret input on STDIN, this is a sound and safe solution, security-wise?
    – cemulate
    Commented Apr 24, 2018 at 20:50
  • I've decided to accept this answer, given that it's the best solution given that you have a well-designed program to work with. sha1pass was just an example in my OP, and clearly not the best choice to use for this.
    – cemulate
    Commented Apr 24, 2018 at 21:00
  • 1
    @cemulate, ...not safe on a command line. In the context of a heredoc or herestring (as in the answer here), it's not exposed to ps, but may be written to a temporary file -- if one wants to avoid that, then sshpass < <(printf '%s\n' "$VARIABLE") might be considered (because printf is a builtin, not an external command, it isn't passed to execv and accessible via ps). Commented Apr 25, 2018 at 17:51
26

If you set HISTCONTROL like so:

HISTCONTROL=ignorespace

and start the command with a space:

~$  mycommand

it won't be stored in the history.

0
11

Pass sensitive data via a pipe or here-doc:

command_with_secret_output | command_with_secret_input

or:

command_with_secret_input <<EOF
$secret
EOF

It's fine for secrets to be in (non-exported) shell variables, but you can never use those variables on command lines, only in here-documents and shell internals.

As noted by Kusalananda in a comment, if you're entering commands in an interactive shell, the lines you enter for a here document will be stored in the shell history, so it's not safe to type a password there, but it should still be safe to use shell variables containing secrets; the history will contain the text $secret rather than whatever $secret expanded to.

Use of command expansions is not safe:

command_with_secret_input "$(command_with_secret_output)"

because the output will be included on the command line and visible in ps output (or manually reading from /proc) except on systems with hardened /proc.

Assignment to a variable is okay too:

secret=$(command_with_secret_output)
12
  • What I'm looking for is an actual command command_with_secret_output that allows me to type the secret output. Does such a command exist that could be substituted here?
    – cemulate
    Commented Apr 23, 2018 at 15:50
  • Note that typing a here-document in an interactive bash session will save the document in the history file.
    – Kusalananda
    Commented Apr 23, 2018 at 15:50
  • @cemulate: Just use the shell builtin read, e.g. read -r secret. Then your secret is in $secret. You can run stty before/after it to hide the input if you like. Commented Apr 23, 2018 at 15:58
  • 3
    @Kusalananda: sha1pass seems fundamentally broken/unusable/unsafe since it can't read the password from stdin or a file. A different utility is needed to solve the problem, I think. Commented Apr 23, 2018 at 16:03
  • 1
    @cemulate Your last comment to R is exactly on the spot. That's why it won't help. read -rs will do the job (if you include -r to be able to have passwords with backslashes in them), but then you're back at potentially showing off the password in the process list (and depending on whether process accounting is turned on and how that's configured, in the process accounting logs as well).
    – Kusalananda
    Commented Apr 24, 2018 at 6:31
6

Just write the value into a file and pass the file:

$ cat > mysecret
Big seecreeeet!
$ cat mysecret | sha1pass 

I'm not sure how sha1pass works, if it can take a file as input, you can use sha1pass < mysecret. If not, using cat might be a problem since it includes the final newline. If that's the case, use (if your head supports -c):

head -c-1 mysecret | sha1pass 
6
  • 2
    Why write the password to disk in your first example when you can just do cat | sha1pass? cat mysecret | sha1pass and sha1pass < mysecret have the same behaviour with respect to final newlines. cat doesn't add newlines. Anyway, if sha1pass supports passing the password through standard input, I would expect it to ignore the final newline by itself. Files with lines that aren't terminated by newlines are unnatural in unix after all, so it'd be awkward to expect a file that is not terminated by a newline.
    – JoL
    Commented Apr 23, 2018 at 16:07
  • @JoL i) because I hadn't thought of that :/ But how would that work? cat | sha1pass seems to run sha1pass before giving me a chance to enter any input. ii) No, of course cat mysecret doesn't add a newline, I never said cat adds it, only that it includes it.
    – terdon
    Commented Apr 23, 2018 at 16:20
  • I see, but it's not like < mysecret removes it either. :)
    – JoL
    Commented Apr 23, 2018 at 16:24
  • Nevermind. Now that I re-read, I see that you said that to later propose head -c-1. I guess I just got confused as to why use cat mysecret | sha1pass and then later propose sha1pass < mysecret. I guess the worry is that sha1pass might complain about regular files for stdin; I'm not sure why.
    – JoL
    Commented Apr 23, 2018 at 16:31
  • @JoL it's more because I've never used sha1pass and couldn't find a manual or -h or any other form of documentation in the few minutes I spent searching and so couldn't figure out if running sha1pass foo treated foo as an input string or as an input file. I therefore gave options to deal with each possibility.
    – terdon
    Commented Apr 23, 2018 at 16:36
1

If what terdon did is possible, then that's the best solution, passing through standard input. The only problem left is he wrote the password to disk. We can do this instead:

stty -echo
echo -n "password: "
head -1 | sha1pass
stty echo

Like Kusalananda said, stty -echo ensures what you type is not seen until you do stty echo again. head -1 will get one line from standard input and pass it on to sha1pass.

1

I would use

sha1pass "$(cat)"

cat would read from stdin until EOF, which can be caused by pressing Ctrl+D. Then, the result would be passed as argument to sha1pass

1
  • 3
    R..'s answer explains why this is unsafe.
    – hvd
    Commented Apr 25, 2018 at 12:10
0

If you are passing sensitive information around and use it regularly you are probably best encrypting it.

Putting something like

#create key as follows - will prompt for password
#echo -n 'secret you want encrypted' | openssl enc -aes-256-cbc  -a -salt -pbkdf2|base64
export MY_SECRET='VTJGc2RHVmtYMTlzVnBGWXNYUitLWlpYT3BWdStaQXJXeUVwc1JORnFsNWswZXJKT1dkRWpsWkxLWVFnK1hONQo='

Into your .bashrc will give you an encrypted environment variable that you can access where ever you need a secret, and you will be prompted for you passphrase/password that you used when creating the environment variable.

In the example above it is 'secret'

You access it is a command as follows

`echo $MY_SECRET|base64 --decode|openssl enc -aes-256-cbc -a -d -salt -pbkdf2 `

e.g.

xfreerpd /parameters.... /p:`echo $MY_SECRET|base64 --decode|openssl enc -aes-256-cbc -a -d -salt -pbkdf2` 

For your query using sha1pass. You can create and passin as follows

MY_SECRET=`echo -n 'secret you want encrypted' | openssl enc -aes-256-cbc  -a -salt -pbkdf2|base64`

Then use it as follows

sha1pass `echo $MY_SECRET|base64 --decode|openssl enc -aes-256-cbc -a -d -salt -pbkdf2 `

The base64 part is so that you can set the variable

MY_SECRET='VTJGc2RHVmtYMTlzVnBGWXNYUitLWlpYT3BWdStaQXJXeUVwc1JORnFsNWswZXJKT1dkRWpsWkxLWVFnK1hONQo='

And not have your secret stuck in the command history etc etc.

When you generate the secret sometimes the base64 output will have multiple lines so you need to take the line breaks out for your variable.

echo -n 'secret you want encrypted' | openssl enc -aes-256-cbc  -a -salt -pbkdf2|base64
enter aes-256-cbc encryption password:
Verifying - enter aes-256-cbc encryption password:
VTJGc2RHVmtYMTlzVnBGWXNYUitLWlpYT3BWdStaQXJXeUVwc1JORnFsNWswZXJKT1dkRWpsWkxL
WVFnK1hONQo=

#take the above line break out 
MY_SECRET='VTJGc2RHVmtYMTlzVnBGWXNYUitLWlpYT3BWdStaQXJXeUVwc1JORnFsNWswZXJKT1dkRWpsWkxLWVFnK1hONQo='

openssh will prompt you for a password to encrypt and decrypt each time, you can supply one as part of the command, but then you are just hiding things from the history etc. Have a look at https://www.tecmint.com/generate-encrypt-decrypt-random-passwords-in-linux/ for some info on using openssh for this. https://www.serverlab.ca/tutorials/linux/administration-linux/how-to-base64-encode-and-decode-from-command-line/ for base64 and https://stackoverflow.com/questions/16072351/how-to-assign-an-output-to-a-shellscript-variable for different options on command substitution I have used back-tick ` above

2
  • If you don't want to be typing passwords in; generate a ssh key pair and use passwordless connections, either within a machine or between machines. Only the public key will be easily observable. Commented Jun 25, 2021 at 11:58
  • There are quite a lot of utilities that take passwords or secrets, and have different qualities in terms of protecting their secrets. For ssh a key pair is much stronger than using passwords. Not all connection methods have this feature, and the more likely you are to want a strong/difficult to type password. Commented Jan 3, 2023 at 16:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .