4

Is it safe to pass a key to the openssl command via the command line parameters in Linux? I know it nulls out the actual parameter, so it can't be viewed via /proc, but, even with that, is there some way to exploit that?

I have a python app that I want to use OpenSSL to do the encryption/description through stdin/stdout streaming in a subprocess, but I want to know my keys are safe.

Travis Jensen
  • 5,362
  • 3
  • 36
  • 40

2 Answers2

5

Passing the credentials on the command line is not safe. It will result in your password being visible in the system's process listing - even if openssl erases it from the process listing as soon as it can, it'll be there for an instant.

openssl gives you a few ways to pass credentials in - the man page has a section called "PASS PHRASE ARGUMENTS", which documents all the ways you can pass credentials into openssl. I'll explain the relevant ones:


env:var

Lets you pass the credentials in an environment variable. This is better than using the process listing, because on Linux your process's environment isn't readable by other users by default - but this isn't necessarily true on other platforms.

The downside is that other processes running as the same user, or as root, will be able to easily view the password via /proc.

It's pretty easy to use with python's subprocess:

new_env=copy.deepcopy(os.environ)
new_env["MY_PASSWORD_VAR"] = "my key data"
p = subprocess.Popen(["openssl",..., "-passin", "env:MY_PASSWORD_VAR"], env=new_env)

fd:number

This lets you tell openssl to read the credentials from a file descriptor, which it will assume is already open for reading. By using this you can write the key data directly from your process to openssl, with something like this:

r, w = os.pipe()
p = subprocess.Popen(["openssl", ..., "-passin", "fd:%i" % r], preexec_fn=lambda:os.close(w))
os.write(w, "my key data\n")
os.close(w)

This will keep your password secure from other users on the same system, assuming that they are logged in with a different account.

With the code above, you may run into issues with the os.write call blocking. This can happen if openssl waits for something else to happen before reading the key in. This can be addressed with asynchronous i/o (e.g. a select loop) or an extra thread to do the write()&close().

One drawback of this is that it doesn't work if you pass closeFds=true to subprocess.Popen. Subprocess has no way to say "don't close one specific fd", so if you need to use closeFds=true, then I'd suggest using the file: syntax (below) with a named pipe.


file:pathname

Don't use this with an actual file to store passwords! That should be avoided for many reasons, e.g. your program may be killed before it can erase the file, and with most journalling file systems it's almost impossible to truly erase the data from a disk.

However, if used with a named pipe with restrictive permissions, this can be as good as using the fd option above. The code to do this will be similar to the previous snippet, except that you'll need to create a fifo instead of using os.pipe():

pathToFifo = my_function_that_securely_makes_a_fifo()
p = subprocess.Popen(["openssl", ..., "-passin", "file:%s" % pathToFifo])
fifo = open(pathToFifo, 'w')
print >> fifo, "my key data"
fifo.close()

The print here can have the same blocking i/o problems as the os.write call above, the resolutions are also the same.

Community
  • 1
  • 1
je4d
  • 7,628
  • 32
  • 46
  • Unfortunately, I'm not looking for ways to pass a password, but an actual key (the -K command line parameter). Using -pass doesn't support that. But this does at least confirm my suspicion that the command line was risky. – Travis Jensen Mar 09 '12 at 00:08
  • @TravisJensen well thanks for accepting anyway! I assume you're using `openssl enc` here - the man page doesn't give any alternative for `-K`, so you're out of luck. But it may be worth a quick look at the source, I wouldn't be too surprised if there's an undocumented alternative. – je4d Mar 09 '12 at 00:16
2

No, it is not safe. No matter what openssl does with its command line after it has started running, there is still a window of time during which the information is visible in the process' command line: after the process has been launched and before it has had a chance to null it out.

Plus, there are many ways for an accident to happen: for example, the command line gets logged by sudo before it is executed, or it ends up in a shell history file.

Openssl supports plenty of methods of passing sensitive information so that you don't have to put it in the clear on the command line. From the manpage:

  • pass:password

    the actual password is password. Since the password is visible to utilities (like 'ps' under Unix) this form should only be used where security is not important.

  • env:var

    obtain the password from the environment variable var. Since the environment of other processes is visible on certain platforms (e.g. ps under certain Unix OSes) this option should be used with caution.

  • file:pathname

    the first line of pathname is the password. If the same pathname argument is supplied to -passin and -passout arguments then the first line will be used for the input password and the next line for the output password. pathname need not refer to a regular file: it could for example refer to a device or named pipe.

  • fd:number

    read the password from the file descriptor number. This can be used to send the data via a pipe for example.

  • stdin

    read the password from standard input.

All but the first two options are good.

Celada
  • 21,627
  • 4
  • 64
  • 78
  • Unfortunately, I'm not looking for ways to pass a password, but an actual key (the -K command line parameter). Using -pass doesn't support that. But this does at least confirm my suspicion that the command line was risky. – Travis Jensen Mar 09 '12 at 00:08