2011-01-10 2 views
12

나는 나의 서버를 관리하는데 도움이되는 데몬을 만들고있다. Webmin은 서버에 쉘을 여는 것과 마찬가지로 잘 작동하지만 디자인 한 UI에서 서버 작업을 제어 할 수 있고 일부 기능을 최종 사용자에게 공개 할 수 있기를 원합니다.루비에서 위생 쉘 명령 또는 시스템 호출하기

데몬은 대기열에서 작업을 선택하여 실행합니다. 그러나 사용자의 의견을 수락 할 것이므로 권한이있는 셸 명령에 위험한 것을 주입 할 수 없도록하고 싶습니다.

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

더 설명하는 요점 : 여기

내 문제 예시 조각의 전형적인 탈출 및 입력의 살균이 경우 충분한 경우 https://gist.github.com/773292

난 긍정적 아니에요을하고있는 디자이너, 나는 보안 관련 경험이별로 없다. 나는 이것이 아마도 나에게 분명해야 할 일인 것을 알고있다. 그러나 그렇지 않다!

작업을 만들고 직렬화하는 웹 응용 프로그램이 위험한 텍스트를 작업을 수신하는 권한있는 프로세스로 전달할 수 없도록하려면 어떻게해야합니까? 그것은 당신처럼 보이지 않는 도움
ARB

답변

17

에 대한

덕분에 당신이 무슨 일을하는지에 대한 쉘이 필요합니다. 여기에 system에 대한 설명서를 참조하십시오. http://ruby-doc.org/core/classes/Kernel.html#M001441

system의 두 번째 형식을 사용해야합니다. 귀하의 위의 예는 될 것입니다 :

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

이 작성하는 더 좋은 (IMO) 방법은 다음과 같습니다

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

이 방법은 execve 전화에 직접 전달되는 인수를, 당신이 필요가 없습니다 비열한 셸 트릭에 대해 걱정하십시오. 당신은 또한 단지 종료 상태하지만 당신은 아마 사용하고자하는 결과를하지 필요한 경우

+0

그래서이 클래스를 노출하고 최종 사용자에게 완전히 필터링하지 않으면 (예 : 사용자 이름으로 "username; rm -rf /") 그림자 해시를 제공한다고 가정 해 봅시다. '/' – arbales

+2

을 제거하는 효과가 있습니다. 인수는 실행 된 프로그램에 직접 전달됩니다. 혼자서 확인할 수 있습니다. 'ruby -e '시스템 * W (ls -l foo; rm -rf /)'' – cam

+0

아를 실행 해보십시오. 그것은 완벽하게 이해가됩니다. 응용 프로그램의 보안을 유지하는 것이 모든 단계에서 위험한 경우가있는 것처럼 간단한 단계와 사실보다 어렵지 않게 자연 스럽 게해야한다는 생각이 있습니다.내가 읽은 적이 없기 때문에 가능성이 높습니다/그것에 대해 많이 알게되었습니다. – arbales

15

Open3.popen3 :

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

더 많은 정보 : Getting output of system() calls in Ruby

2

나는 'shellwords'모듈로 보는 게 좋을 것 . 이 스크립트 :

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

는 탈출 텍스트와 예상 출력을 출력합니다

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

이 오래된 질문이지만, 거의 유일한 진짜 대답은 이후 내가 생각 인터넷 검색을 할 때 찾을 수 있습니다 d 경고를 추가하십시오. 다중 인수 버전의 시스템은 Linux에서는 상당히 안전하지만 Windows에서는 그렇지 않습니다.

system "dir", "&", "echo", "hi!"Windows 시스템에서 시도하십시오. dir과 echo 모두 실행됩니다. 에코는 당연히 훨씬 덜 무해한 무언가가 될 수 있습니다.

0

나는 이것이 오래된 스레드라는 것을 알고 있지만, 가볍게 닿은 다른 옵션은 Simon Hürlimann입니다.

이 주제에 대한 많은 정보가 없으며 이것이 도움이 필요한 다른 사람들을 도울 수 있다고 생각합니다. 우리가 PID 당신에게 동기 또는 비동기 명령을 실행할 수있는 기능을 제공하고, 표준 출력, 열려진, 종료 코드을 제공 Open3을 사용합니다 예를 들어

.

Open3은 stdout, stderr, 종료 코드 및 다른 프로그램을 실행할 때 하위 프로세스를 대기하는 스레드에 액세스 할 수있게합니다. Process.spawn과 같은 방법으로 프로그램의 다양한 속성, 방향 전환, 현재 디렉토리 등을 지정할 수 있습니다. (출처 : Open3 Docs)

출력 형식을 CommandStatus 개체로 지정했습니다. 여기에는 stdout, stderr, pid (작업 스레드 중) 및 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}" 

표준 출력/ERR 버퍼를 읽는 동안

, I는 command_stdout 변수를 할당할지 여부를 제어하는 ​​command_stdout = stdout_buffer if stdout_buffer.length > 0를 사용한다. 데이터가없는 경우 "" 대신 nil을 전달해야합니다. 나중에 데이터를 전달할 때 더 명확합니다.

아마 command + ';'을 사용하고있는 것으로 나타났습니다. 첫 번째 양식에서 문자열 (간부 ("명령"))이 간단한 규칙을 따르는 경우

: 그 이유는 (popen3 사용하는 것입니다) Kernel.exec의 설명서를 기반으로

  • 에는 메타 문자가없는
  • 없는 쉘 예약어없이 특수 내장
  • 루비 직접 쉘없이 명령을 호출

";"을 추가하여 쉘 호출을 강제 실행할 수 있습니다. 문자열에, 단순히 당신이 잘못된 명령을 전달할 경우 'spawn': No such file or directory 오류를 던지고에서 루비를 방지이

( 때문에 ""메타 문자입니다). 대신 오류를 정상적으로 해결하고 포착되지 않은 예외 대신 STDERR로 나타나는 커널로 직접 전달합니다.