Довольно способ хранения конфиденциальной информации из зарегистрированной командной строки в Ruby?
У меня есть длинная команда, которую я строю с лопатами (<<
), с намерением в конечном итоге запустить system(command)
. Я хотел бы зарегистрировать командную строку, чтобы убедиться, что она построена правильно, перед выполнением команды. Однако команда должна содержать учетные данные, которые я не хочу регистрировать. Я ищу элегантный способ удовлетворить оба требования (1. запишите мясо командной строки, 2. не регистрируйте его учетные данные, которые он содержит).
Это то, что у меня есть сейчас:
command = ""
public = ""
[command, public].each {|str| str << "command_name"}
[command, public].each {|str| str << " -a a"}
[command, public].each {|str| str << " -b b"}
[command, public].each {|str| str << " -c c"}
command << " -d d"
command << " -e e"
[command, public].each {|str| str << " -f f"}
command << " -g g"
[command, public].each {|str| str << " -h h"}
[command, public].each {|str| str << " -i i"}
puts command
=> 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)
3 ответа
Я вижу еще две проблемы безопасности с этим кодом.
-
Выполнение строки, а не массива команд и параметров
При создании командной строки программно опасно создавать ее как строку, особенно когда любой из параметров поступает из пользовательского ввода. При вызове
Kernel#system
со строкой, оболочка интерпретирует командную строку; при вызове с массивом ядро операционной системы выполняет команду напрямую.В случае доброкачественной ошибки пароль может содержать пробел , и в этом случае оболочка интерпретирует его как два параметра, что может привести к неправильной выработке команды. В худшем случае пользователь может вызвать уязвимость произвольного выполнения команды.
В качестве иллюстрации следующий код будет делать больше, чем просто установить пароль на
"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)
Практика более безопасных вычислений! Это не уязвимо для выполнения произвольной команды:
command = ['htpasswd', '-b', '-c', password_file, username, password] system(*command)
-
Передача конфиденциальной информации в командной строке
Даже если вам удастся подвергнуть цензуре регистратор в Ruby, по-прежнему считается опасной практикой передавать любую конфиденциальную информацию в качестве параметра командной строки. Командные строки видны всем пользователям на машине .
По этой причине все разумные команды должны предлагать альтернативный механизм приема секретной информации. Например, рекомендуемый способ использования
htpasswd
, так как Apache 2.4 должен читать пароль со стандартного ввода.command = ['htpasswd', '-i', '-c', password_file, username] IO.popen(command, mode='w') do |htpasswd| htpasswd.puts(password) end
Аналогично, вот пример того, как безопасно указать кодовую фразу для GnuPG (на Python) .
В заключение, передавая секретную информацию через каналы вместо командной строки , вы решаете как проблему цензуры журнала , так и проблему видимости командной строки в командной строке .
Кажется, что вы многократно выполняете много операций как для «команды», так и «public». То, как связаны эти переменные, заставляет меня думать, что они должны быть частью одного и того же объекта. Например:
class Command
attr_reader :command, :loggable
def initialize(command)
@command = command
@loggable = command
end
def concat(option)
@command << ' ' + option
@loggable << ' ' + option
end
alias_method :<<, :concat
def concat_private(option)
@command << ' ' + option
end
alias_method :to_s, :command
alias_method :to_str, :command
end
command = Command.new('echo')
command << '-a this'
command << '-b that'
command.concat_private '--password asdf'
system(command)
Этот подход также делает ваш код более надежным, дает вам гибкость добавления дополнительных методов в Command
, если возникнет такая необходимость.
Я полностью согласен со всем, что сказал @ 200_success, но если вы хотите получить конфиденциальное информационное решение, я бы предложил нечто большее в строках html_safe
- специализированная строка, которая создается по умолчанию и отображается только при явной просьбе:
class PrivateString < String
alias to_private_s to_s
def to_s
'*******'
end
def concat(other)
if other.respond_to?(:to_private_s)
super(other.to_private_s)
else
super
end
end
alias << concat
end
class String
def private
PrivateString.new(self)
end
alias unsafe_concat concat
def concat(other)
if other.respond_to?(:to_private_s)
unsafe_concat(other.to_s)
else
unsafe_concat(other)
end
end
alias << concat
end
Теперь ваш код будет выглядеть так:
command = "".private
public = ""
[command, public].each {|str| str << "command_name"}
[command, public].each {|str| str << " -a a"}
[command, public].each {|str| str << " -b b"}
[command, public].each {|str| str << " -c c"}
[command, public].each {|str| str << " -d d".private}
[command, public].each {|str| str << " -e e".private}
[command, public].each {|str| str << " -f f"}
[command, public].each {|str| str << " -g g".private}
[command, public].each {|str| str << " -h h"}
[command, public].each {|str| str << " -i i"}
puts command
=> 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)
Это, конечно же, реализация с голыми костями, и, вероятно, ее следует развивать дальше (вы можете увидеть реализацию html_safe
здесь ), но он позволяет вы обрабатываете частные строки, как и любую другую строку, и только обращаетесь к ней как к приватной, когда это необходимо.