2011-01-10 3 views
12

私は自分のサーバーの管理に役立つデーモンを構築しています。 Webminはうまく動作しますが、サーバーへのシェルを開くだけですが、私が設計したUIからサーバー操作を制御できるようになり、エンドユーザーにいくつかの機能が公開されます。Rubyでのサニタリーシェルコマンドまたはシステムコールの作成

デーモンは、アクションをキューから取り出して実行します。しかし、私はユーザーからの入力を受け付けるので、特権シェルコマンドに何か危険なものを注入することはできません。

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

詳細説明要旨:ここ

は私の問題を例示フラグメントですhttps://gist.github.com/773292

私は入力の典型的なエスケープや消毒、この場合のために十分であれば、正はないよ、とされてデザイナー、私はセキュリティ関連の経験がありません。 私はこれがおそらく私には明らかであるべきであることを知っています、しかしそうではありません!

アクションを作成してシリアル化するWebアプリケーションが、アクションを受け取った特権プロセスに危険なテキストを渡すことができないようにするにはどうすればよいですか?それはあなたのように見えない助け
ARB

答えて

17

ため

おかげで、あなたがやっていることのためにシェルを必要としています。 systemのドキュメントを参照してください。http://ruby-doc.org/core/classes/Kernel.html#M001441

systemの2番目の形式を使用してください。あなたの例では、上記になる:

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 'system * 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

これは、古い質問ですが、それはかなりの唯一の本当の答えなので、私は私と思っグーグルとき、あなたは見つけることができます警告を付けます。マルチ・アーギュメント・バージョンのシステムは、Linux上ではかなり安全だと思われますが、Windows上ではありません。

Windowsシステムでsystem "dir", "&", "echo", "hi!"を試してみてください。 dirとechoの両方が実行されます。エコーは、もちろん無害なものではありません。

0

私はこれが古いスレッドだと知っていますが、軽く触れられた別のオプションがあります。SimonHürlimann

このトピックに関する多くの情報はありません。これは、必要がある他の人に役立つと思います。

この例では、我々はあなたの同期または非同期のコマンドを実行することができますし、標準出力を提供し、stderrの終了コード、およびPIDOpen3を使用します。

Open3は、別のプログラムを実行しているときに、子プロセスを待つためにstdout、stderr、終了コードとスレッドにアクセスします。 Process.spawnと同じ方法で、プログラムのさまざまな属性、リダイレクト、現在のディレクトリなどを指定できます。 (ソース:Open3 Docs

出力をCommandStatusオブジェクトとしてフォーマットすることを選択しました。これには、stdout,stderr,pid(ワーカースレッドの)とexitstatusが含まれています。 STDOUT/ERRバッファを読んでいる間

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}" 

、私はcommand_stdout変数が割り当てられているかどうかを制御するcommand_stdout = stdout_buffer if stdout_buffer.length > 0を使用しています。データが存在しない場合は""の代わりにnilを渡す必要があります。後でデータを渡すときはより明確です。

あなたはおそらく私にcommand + ';'を使用していることに気づいたでしょう。最初の形式から文字列(のexec(「コマンド」))これら の単純なルールに従うならば

:この理由は、(popen3が使用するものである)Kernel.execからの文書に基づいています

  • なしメタ文字
  • 何のシェル予約語はなく、特別なビルトイン
  • Rubyはシェル
せずに直接コマンドを起動します210

シェルの呼び出しを強制するには、 ";"文字列に、単にあなたが不正なコマンドを渡すと'spawn': No such file or directoryエラーを投げてからルビーを防ぐこの

( ので、「」メタ文字があります)。代わりに、エラーを正常に解決し、キャッチされない例外ではなくSTDERRとして表示されるカーネルに直接渡します。

関連する問題