HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyModerate

Pretty way of keeping sensitive info out of a logged command string in Ruby?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
keepinginfoprettywayrubyloggedsensitivestringoutcommand

Problem

I have a long command that I am building with shovels (<<), with the intention of eventually running system(command). I'd like to log the command string, to make sure it is built properly, right before I execute the command. However, the command has to contain account credentials that I don't want to log. I'm looking for an elegant way to satisfy both requirements (1. log the meat of the command string, 2. don't log the auth credentials it contains).

This is what I have now:

command = ""
public = ""

[command, public].each {|str| str  command_name -a a -b b -c c -d d -e e -f f -g g -h h -i i
puts public
=> command_name -a a -b b -c c -f f -h h -i i

system(command)


Original question from Stack Overflow

Solution

I see two more security issues with this code.

-
Executing a string rather than an array of command and parameters

When building a command line programmatically, it is dangerous to build it as a string, especially when any of the parameters comes from user input. When calling Kernel#system with a string, a shell interprets the command line; when calling it with an array, the operating system kernel executes the command directly.

In the benign failure case, the password might contain a space, in which case the shell would interpret it as two parameters, probably causing your command to be invoked incorrectly. In the worst case, the user could trigger an arbitrary command execution vulnerability.

As an illustration, the following code will do more than just set your password to "hello":

password_file = '.htpasswd'
username = 'CHK'
password = 'hello; touch me; grep; unzip; strip; finger; fsck -y; more; yes; yes; yes; umount; sleep'
command = "htpasswd -b -c #{password_file} #{username} #{password}"
system(command)


Practice safer computing! This is not vulnerable to arbitrary command execution:

command = ['htpasswd', '-b', '-c', password_file, username, password]
system(*command)


-
Passing sensitive information on the command line

Even if you manage to censor the logger within Ruby, it's still considered dangerous practice to pass any sensitive information as a command-line parameter. Command lines are visible to all users on a machine.

For that reason, all reasonable commands should offer an alternative mechanism to accept secret information. For example, the recommended way to use htpasswd, since Apache 2.4, is to read the password from standard input.

command = ['htpasswd', '-i', '-c', password_file, username]
IO.popen(command, mode='w') do |htpasswd|
  htpasswd.puts(password)
end


Similarly, here is an example of how to securely specify a passphrase to GnuPG (in Python).

In summary, by passing the secret information through pipes instead of the command line, you solve both the log censorship problem and the machine-wide command-line visibility problem.

Code Snippets

password_file = '.htpasswd'
username = 'CHK'
password = 'hello; touch me; grep; unzip; strip; finger; fsck -y; more; yes; yes; yes; umount; sleep'
command = "htpasswd -b -c #{password_file} #{username} #{password}"
system(command)
command = ['htpasswd', '-b', '-c', password_file, username, password]
system(*command)
command = ['htpasswd', '-i', '-c', password_file, username]
IO.popen(command, mode='w') do |htpasswd|
  htpasswd.puts(password)
end

Context

StackExchange Code Review Q#47978, answer score: 19

Revisions (0)

No revisions yet.