Safe way to pass password for >1 programs in bash

40,697

Solution 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 '%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

Solution 2

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

)

Solution 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
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
Share:
40,697

Related videos on Youtube

clerksx
Author by

clerksx

Hi there! I work on Linux kernel memory management and also help maintain systemd. You can find me at: chrisdown.name GitHub LinkedIn

Updated on September 18, 2022

Comments

  • clerksx
    clerksx about 1 year

    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?

  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' almost 12 years
    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.)
  • Admin
    Admin almost 12 years
    As I understand from another your answer, in the bash version with env:PASS is safe too.
  • Admin
    Admin almost 12 years
    printf '%s\n' "$PASS" isn't safe. Somebody can read the command line with ps for example.
  • bsd
    bsd almost 12 years
    Thanks @Gilles, looked at the docs and saw my blunder, updated answer with a different approach.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' almost 12 years
    @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.
  • gaoithe
    gaoithe almost 4 years
    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 ?
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' almost 4 years
    @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).