Safe way to pass password for >1 programs in bash

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?


Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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 '%sn' "$PASS" |
openssl enc -d -aes-256-cbc -kfile /dev/stdin -in file.old |
sed ... | {
  printf '%sn' "$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 '%sn' "$PASS" | {
  printf '%sn' "$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

Method 2

Using Bash it can be done without using printf '%sn' "$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


Method 3

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

# edit intermediate

# encode intermediate back into file
openssl <<HERE 2>&1 >/dev/null
enc -e -aes-256-cbc -k "$PASS" -in intermediate -out file 
unset PASS
rm -f intermediate

Method 4

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

tfile=$(mktemp /tmp/config.XXXXXXXXX)
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
   echo "Warning: No git config file found. Aborting.";exit;

All methods was sourced from or, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Notify of
Inline Feedbacks
View all comments