patternrubyModerate
Pretty way of keeping sensitive info out of a logged command string in Ruby?
Viewed 0 times
keepinginfoprettywayrubyloggedsensitivestringoutcommand
Problem
I have a long command that I am building with shovels (
This is what I have now:
Original question from Stack Overflow
<<), 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
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
Practice safer computing! This is not vulnerable to arbitrary command execution:
-
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
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.
-
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)
endSimilarly, 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)
endContext
StackExchange Code Review Q#47978, answer score: 19
Revisions (0)
No revisions yet.