23

I'm writing a bash script, and need to ask user for his password and pass it to openssl. Whilst openssl can read the password itself, I need for two runs of the program and don't want to ask the user twice. Here is the script:

cp file{,.old}
read -sp 'Enter password. ' PASS; echo
export PASS

# decode | edit | encode
openssl enc -d -aes-256-cbc -k "$PASS" -in file.old | \
  sed ... | openssl enc -e -aes-256-cbc -k "$PASS" -out file

unset PASS

This is not safe as the password is easily available by looking at the command line; somebody can read it using ps, for example.

openssl can read a password from an environment variable, so I can replace -k "$PASS" with -pass env:PASS, but it's still not safe; the environment variables of any process can be read freely (again, ps can do it).

So, how can I safely pass the password to the two openssl instances?

Chris Down
  • 122,090
  • 24
  • 265
  • 262
  • combination of GnuPG and PinEntry can be used here http://www.gnupg.org/related_software/pinentry/index.en.html – Nikhil Mulley Jan 14 '12 at 12:37
  • 1
    "the environment variables of any process can be read freely" - this is not entirely correct. `ps` reads the environment of a process from `/proc//environ`, but this file has `0600` permissions, so only root and the user running the process are able to read the environment of the process. I'd say that's pretty safe. – Martin von Wittich Dec 02 '16 at 17:01

4 Answers4

17

Pass the password on a separate file descriptor from the input (twice, once for encryption and once for decryption). Do not export PASS to the environment.

read -sp 'Enter password. ' PASS
printf '%s\n' "$PASS" |
openssl enc -d -aes-256-cbc -kfile /dev/stdin -in file.old |
sed ... | {
  printf '%s\n' "$PASS" |
  openssl enc -e -aes-256-cbc -kfile /dev/stdin -in /dev/fd/3 -out file;
} 3<&0

If your system doesn't have /dev/fd, you can use the -pass argument to tell openssl to read the passphrase from an open file descriptor.

printf '%s\n' "$PASS" | {
  printf '%s\n' "$PASS" |
  openssl enc -d -aes-256-cbc -pass fd:0 -in file.old |
  tr a-z A-Z | tee /dev/tty | {
  openssl enc -e -aes-256-cbc -pass fd:3 -out file; }
} 3<&0
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • As I understand from [another your answer](http://unix.stackexchange.com/a/29176/14284), in the `bash` version with `env:PASS` is safe too. –  Jan 15 '12 at 06:32
  • `printf '%s\n' "$PASS"` isn't safe. Somebody can read the command line with `ps` for example. –  Jan 15 '12 at 06:38
  • 8
    @user14284 No, and no. `env:PASS` is not safe because the password would appear in the environment of the `openssl` process (it wouldn't appear in the environment of the `bash` process, but that's not enough). Using `printf` is safe because it's a bash built-in. – Gilles 'SO- stop being evil' Jan 15 '12 at 17:01
  • echo is a bash built in, so would not a simple echo command be safe? ```echo $PASS | openssl ...```. It would not appear in ps listing. The only place you can get pass would be in bash process memory. I think ? – gaoithe Dec 09 '19 at 17:03
  • 1
    @gaoithe Yes, `echo` would be safe for the same reason `printf` is safe (and `printf` would not be safe in a shell where it is not built in). The reason I use `printf` and not `echo` is that `echo` may mangle backslashes (depending on the bash options). – Gilles 'SO- stop being evil' Dec 09 '19 at 18:08
9

Using Bash it can be done without using printf '%s\n' "$PASS" by associating a so-called here string with file descriptors using the Bash builtin exec command.

For more information see: Shell script password security of command-line parameters.

(

# sample code to edit password-protected file with openssl
# user should have to enter password only once
# password should not become visible using the ps command

echo hello > tmp.file

#env -i bash --norc   # clean up environment
set +o history
unset PASS || exit 1

read -sp 'Enter password. ' PASS; echo

# encrypt file and protect it by given password
exec 3<<<"$PASS"
openssl enc -e -aes-256-cbc -pass fd:3  -in tmp.file -out file

cp file{,.old}

# decode | edit | encode
exec 3<<<"$PASS" 4<<<"$PASS"
openssl enc -d -aes-256-cbc -pass fd:3 -in file.old | 
   sed 's/l/L/g' | 
   openssl enc -e -aes-256-cbc -pass fd:4 -out file

exec 3<<<"$PASS"
openssl enc -d -aes-256-cbc -pass fd:3 -in file

rm -P tmp.file file.old
unset PASS

)
jon
  • 91
  • 1
1

Sorry, my previous answer was from openssl man, not the openssl enc docs.

This solution is not a pipeline, but I believe this solution prevents the password from being visible to ps.

Using a here document, only openssl sees the text of the password.
As long as you're certain to eliminate the intermediate file, no trace remains. Maybe someone can help do this in a pipeline and eliminate the intermediate file?

# cp file{,.old}  don't need this anymore since intermediate becomes same
read -sp 'Enter password. ' PASS; echo
#no need to export, env's are readable, as mentioned

# decode into intermediate file
openssl <<HERE 2>&1 >/dev/null
enc -d -aes-256-cbc -k "$PASS" -in file -out intermediate
HERE

# edit intermediate

# encode intermediate back into file
openssl <<HERE 2>&1 >/dev/null
enc -e -aes-256-cbc -k "$PASS" -in intermediate -out file 
HERE
unset PASS
rm -f intermediate
bsd
  • 10,916
  • 4
  • 30
  • 38
  • This would be a better answer if it explained how to use the switch. It's not wrong (except that the `enc` command has no `-kn` switch, at least on current versions, it's `-pass`), but not very informative. (The downvote isn't mine.) – Gilles 'SO- stop being evil' Jan 15 '12 at 02:25
  • Thanks @Gilles, looked at the docs and saw my blunder, updated answer with a different approach. – bsd Jan 15 '12 at 12:54
0

I created this little bash script to do a complete reset preserving the config file. The forced push effectively recreates your GitHub repo.

#!/bin/bash
tfile=$(mktemp /tmp/config.XXXXXXXXX)
GITCONF=".git/config"
commitmsg=${1:-git repository initialised}
if [ -f $GITCONF ]; then
   mv .git/config tfile
   rm -rf .git
   git init .
   mv tfile .git/config
   git add .
   git commit -a -m "${commitmsg}"
   git push -f
else
   echo "Warning: No git config file found. Aborting.";exit;
fi
RichieHH
  • 99
  • 3