sistema rails procesos para llamadas comandos administraciĆ³n ruby security shell system sanitization

ruby - rails - llamadas al sistema linux



FormaciĆ³n de comandos de shell sanitario o llamadas al sistema en Ruby (5)

No parece que necesites un caparazón para lo que estás haciendo. Consulte la documentación del system aquí: http://ruby-doc.org/core/classes/Kernel.html#M001441

Deberías usar la segunda forma de system . Su ejemplo anterior se convertiría en:

system ''usermod'', ''-p'', @options[''shadow''], @options[''username'']

Una manera más agradable (IMO) de escribir esto es:

system *%W(usermod -p #{@options[''shadow'']} #{@options[''username'']})

Los argumentos de esta manera se pasan directamente a la llamada execve , por lo que no tiene que preocuparse por los trucos de shell astuto.

Estoy construyendo un daemon que me ayudará a administrar mi (s) servidor (es). Webmin funciona bien, al igual que abrir un shell para el servidor, pero prefiero poder controlar las operaciones del servidor desde una interfaz de usuario que diseño, y también exponer algunas funciones a los usuarios finales.

El daemon recogerá acciones de una cola y las ejecutará. Sin embargo, dado que aceptaré los comentarios de los usuarios, quiero asegurarme de que no se les permita inyectar algo peligroso en un comando de shell privilegiado.

Aquí hay un fragmento que ejemplifica mi problema:

def perform system "usermod -p #{@options[''shadow'']} #{@options[''username'']}" end

Una esencia que explica más: https://gist.github.com/773292

No estoy seguro si el escape típico y la desinfección de los insumos son suficientes para este caso, y al ser diseñador, no tengo mucha experiencia relacionada con la seguridad. Sé que esto es algo que probablemente debería ser obvio para mí , ¡pero no lo es!

¿Cómo puedo asegurarme de que la aplicación web que creará y serialice las acciones no pueda pasar texto peligroso al proceso privilegiado que recibe las acciones?

Gracias por la ayuda
arb


Sugiero buscar en el módulo ''shellwords''. Este script:

require ''shellwords'' parts = [''echo'', "''hello world''; !%& some stuff", ''and another argument''] command = Shellwords.shelljoin( parts ) puts command output = `#{ command }` puts output

genera el texto escapado y el resultado esperado:

echo /'hello/ world/'/;/ /!/%/&/ some/ stuff and/ another/ argument ''hello world''; !%& some stuff and another argument


Esta es una vieja pregunta, pero ya que es casi la única respuesta real que encontrarás al buscar en Google, pensé que agregaría una advertencia. La versión multi-argumento del sistema parece razonablemente segura en Linux, pero NO en Windows.

Prueba el system "dir", "&", "echo", "hi!" en un sistema de Windows. Tanto dir como echo se ejecutarán. Eco podría, por supuesto, ser algo mucho menos inofensivo.


Sé que este es un hilo viejo, pero hay otra opción que fue ligeramente tocada por Simon Hürlimann .

No hay mucha información sobre este tema y creo que esto podría ayudar a otros que lo necesiten.

Para este ejemplo, utilizaremos Open3 que le brinda la capacidad de ejecutar comandos de forma síncrona o asincrónica, y proporciona stdout , stderr , códigos de salida y PID .

Open3 le otorga acceso a stdout, stderr, códigos de salida y un hilo para esperar el proceso secundario cuando ejecuta otro programa. Puede especificar varios atributos, redirecciones, directorio actual, etc., del programa de la misma manera que para Process.spawn. ( Fuente: Open3 Docs )

Elegí formatear la salida como un objeto CommandStatus . Este contiene nuestro stdout , stderr , pid (Del hilo de trabajo) y exitstatus .

class Command require ''open3'' class CommandStatus @stdout = nil @stderr = nil @pid = nil @exitstatus = nil def initialize(stdout, stderr, process) @stdout = stdout @stderr = stderr @pid = process.pid @exitstatus = process.exitstatus end def stdout @stdout end def stderr @stderr end def exit_status @exitstatus end def pid @pid end end def self.execute(command) command_stdout = nil command_stderr = nil process = Open3.popen3(ENV, command + '';'') do |stdin, stdout, stderr, thread| stdin.close stdout_buffer = stdout.read stderr_buffer = stderr.read command_stdout = stdout_buffer if stdout_buffer.length > 0 command_stderr = stderr_buffer if stderr_buffer.length > 0 thread.value # Wait for Process::Status object to be returned end return CommandStatus.new(command_stdout, command_stderr, process) end end cmd = Command::execute("echo {1..10}") puts "STDOUT: #{cmd.stdout}" puts "STDERR: #{cmd.stderr}" puts "EXIT: #{cmd.exit_status}"

Mientras leo los búferes STDOUT / ERR, uso command_stdout = stdout_buffer if stdout_buffer.length > 0 para controlar si la variable command_stdout está asignada o no. Debería pasar nil lugar de "" cuando no hay datos presentes. Es más claro cuando se entregan datos más adelante.

Probablemente me has notado usando el command + '';'' . La razón para esto se basa en la documentación de Kernel.exec (que es lo que utiliza popen3):

Si la cadena de la primera forma (exec ("comando")) sigue estas reglas simples:

  • sin metacaracteres
  • sin shell palabra reservada y sin función especial incorporada
  • Ruby invoca el comando directamente sin shell

Puede forzar la invocación del shell agregando ";" a la cadena (porque ";" es un meta caracter)

Esto simplemente evita que un Ruby arroje un ''spawn'': No such file or directory error de ''spawn'': No such file or directory si pasa un comando mal formado. En su lugar, pasará directamente al núcleo donde el error se resolverá correctamente y aparecerá como STDERR en lugar de una excepción no detectada.


Si necesita no solo el estado de salida sino también el resultado, probablemente quiera utilizar Open3.popen3 :

require ''open3'' stdin, stdout, stderr = Open3.popen3(''usermod'', ''-p'', @options[''shadow''], @options[''username'']) stdout.gets sterr.gets

Más información aquí: Obtención de salida de llamadas al sistema () en Ruby