6

Notice: the very same vulnerability has been discussed in this question, but the different setting of the problem (in my case I don't need to store the passphrase) allows for a different solution (i.e. using file descriptors instead of saving the passphrase in a file, see ilkkachu's answer).

Suppose I have a symmetrically encrypted file my_file (with gpg 1.x), in which I store some confidential data, and I want to edit it using the following script:

read -e -s -p "Enter passphrase: " my_passphrase
gpg --passphrase $my_passphrase --decrypt $my_file | stream_editing_command | gpg --yes --output $my_file --passphrase $my_passphrase --symmetric
unset my_passphrase

Where stream_editing_command substitutes/appends something to the stream.

My question: is this safe? Will the variable $my_passphrase and/or the decrypted output be visible/accessible in some way? If it isn't safe, how should I modify the script?

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
francescop21
  • 288
  • 5
  • 14
  • 2
    see also https://unix.stackexchange.com/q/400772/30851 – frostschutz Sep 17 '18 at 10:17
  • 3
    In addition to passphrase leaking to the process list, your command chain might overwrite the file before you had a chance to decrypt it, resulting in data loss. – frostschutz Sep 17 '18 at 10:22
  • 4
    Regarding the "disclaimer" that you added. It is the same issue. You store the passphrase in a variable and use it on the command line when calling `gpg`. It will therefore be visible in the output of `ps`. It is not possible to call a program with a password on the command line securely. This is why `gpg` and other programs that takes passwords often read them from a file. A file can be made read protected for anyone but the file's owner (and root). – Kusalananda Sep 17 '18 at 19:42
  • 1
    There is a slight difference between this and the linked question: That one asks about running `gpg` through `cron`, which pretty much forces using some sort of persistent storage for the passphrase (i.e. a file). – ilkkachu Sep 17 '18 at 20:24

1 Answers1

8
gpg --passphrase $my_passphrase 

My question: is this safe? Will the variable $my_passphrase and/or the decrypted output be visible/accessible in some way?

No, that's not really considered safe. The passphrase will be visible in the output of ps, just like all other running processes' command lines. The data itself will not be visible, the pipe is not accessible to other users.

The man page for gpg has this to say about --passphrase:

--passphrase string    

Use string as the passphrase. This can only be used if only one passphrase is supplied. Obviously, this is of very questionable security on a multi-user system. Don't use this option if you can avoid it.

Of course, if you have no other users on the system and trust none of your services have been compromised there should be no-one looking at the process list.

But in any case, you could instead use --passphrase-fd and have the shell redirect the passphrase to the program. Using here-strings:

#!/bin/bash
read -e -s -p "Enter passphrase: " my_passphrase
echo        # 'read -s' doesn't print a newline, so do it here
gpg --passphrase-fd 3 3<<< "$my_passphrase" --decrypt "$my_file" |
    stream_editing_command |
    gpg --yes --output "$my_file" --passphrase-fd 3 3<<< "$my_passphrase" --symmetric

Note that that only works if the second gpg doesn't truncate the output file before getting the full input. Otherwise the first gpg might not get to read the file before it's truncated.


To avoid using the command line, you could also store the passphrase in a file, and then use --passphrase-file. But you'd then need to be careful about setting up the access permissions of the file, to remove it afterwards, and to choose a proper location for it so that the passphrase doesn't get actually stored on persistent storage.

ilkkachu
  • 133,243
  • 15
  • 236
  • 397
  • I assume that the output-being-truncated issue is avoided if another file is used for the output of the second `gpg` command (*i.e.* `gpg --yes --output "$another_file"`), am I right? – francescop21 Sep 19 '18 at 08:10
  • 1
    @francescop21, yep. Or pipe the result through [`sponge`](https://linux.die.net/man/1/sponge). – ilkkachu Sep 19 '18 at 09:12
  • @ilkkachu In `gpg --passphrase-fd 3 3<<< "$my_passphrase" --decrypt "$my_file"`, is `my_passphrase` stored as environment variable for the script? Is it safe to store it like that? It would be available in the bash history and also while (or after) assigning the variable, would it be available to other processes? – Porcupine Jun 30 '21 at 02:05
  • @Porcupine, `my_passphrase` would be a shell variable, in the memory of the shell (plus where ever it was read from). It would get exported in the environment of other processes only if you run `export my_passphrase` in the script. It's not that easy to read the memory of a process, or the environment variables. Stuff on the command line is a different thing, since by default on Linux, the command line of a process can be read by _any user_ on the system (via `/proc`, and with stuff like `ps`). – ilkkachu Jun 30 '21 at 08:45
  • @ilkkachu Forgive me, for I am a noob. If your above script is called as `A.sh`. Do you execute it like `my_passphrase="xxxxxxxx" bash A.sh`? Is this secure? (PS: How else can we run your script?) – Porcupine Jun 30 '21 at 10:45
  • @Porcupine, ah, right, the question had the `read` command which asks the user to input the passphrase. I skipped that part from the answer, but added the hashbang probably to mark that `<<<` isn't a standard sh feature, but requires Bash/Ksh/Zsh. But yes, `foo=bar somecommand abcd` would export `foo` as an envvar, and `somecommand abcd` would show in the command line. You could try something like `foo=bar sleep 1234 & tr '\0' '\n' /proc/$!/cmdline`. And the same for `.../environ`. `cmdline` is readable by anyone by default, `environ` by only the user. – ilkkachu Jun 30 '21 at 17:30
  • (`$!` is the process id of the last background process, and you could just `cat /proc/$pid/cmdline`, but it contains NUL bytes as separators, and they don't show when printing, so the `tr` might make it more readable.) – ilkkachu Jun 30 '21 at 17:38
  • @ilkkachu Your read statement uses `-e`. I checked that it is used for: `If the standard input is coming from a terminal, readline is used to obtain the line.` But, is it necessary? Does this have any important significance? Also your `tr` command is showing this error `tr: extra operand ‘/proc/53615/cmdline’` – Porcupine Jul 01 '21 at 14:57
  • @Porcupine, sorry, that should be `tr '\0' '\n' < /proc/$!/cmdline` . The `read` command is from the question, not mine, and no, the `-e` isn't necessary, just gives nicer line editing (which probably isn't that useful with `-s` which hides the entered text, but whatever) – ilkkachu Jul 01 '21 at 18:25