2010-01-11 30 views
16

Tôi muốn cho phép người dùng tạo tập lệnh Ruby thực hiện tính toán trên một số dữ liệu nằm trên máy chủ web và sau đó xuất kết quả. Các tập lệnh được thực thi trên máy chủ. Có cách nào để thực hiện điều này một cách an toàn không?Thực thi mã ruby ​​do người dùng cung cấp trên máy chủ web

Cụ thể hơn, tôi muốn:

  • hạn chế nguồn kịch bản có thể sử dụng (bộ nhớ và CPU), và hạn chế thời gian chạy
  • hạn chế mà cốt lõi lớp kịch bản có thể sử dụng (ví dụ như chuỗi , Fixnum, phao, Toán vv)
  • hãy truy cập kịch bản và trả lại dữ liệu
  • đầu ra bất kỳ lỗi nào cho người dùng

Có thư viện hoặc dự án nào làm những gì tôi yêu cầu không? Nếu không phải trong Ruby, có lẽ một số ngôn ngữ khác?

Trả lời

17

Bạn có thể sử dụng một "phiến đá trắng" như một căn phòng sạch sẽ, và một sandbox, trong đó để thiết lập các safe level đến 4.

Một phiến đá trắng một đối tượng bạn đã bị tước tất cả các phương pháp từ:

class BlankSlate 

    instance_methods.each do |name| 
    class_eval do 
     unless name =~ /^__|^instance_eval$|^binding$|^object_id$/ 
     undef_method name 
     end 
    end 
    end 

end 

một phòng sạch là một đối tượng, trong đó bối cảnh mà bạn đánh giá các mã khác:

clean_room = BlankSlate.new 

đọc một lệnh từ một nguồn không tin cậy, sau đó untaint nó. Trừ khi không được sơn, Ruby sẽ từ chối đánh giá chuỗi trong hộp cát.

command = gets 
    command.untaint 

Bây giờ, hãy thực hiện chuỗi trong hộp cát, nâng mức an toàn lên cao như nó sẽ đi. Mức $ SAFE sẽ trở lại bình thường khi proc kết thúc. Chúng ta thực hiện lệnh trong bối cảnh ràng buộc của phòng sạch, để nó chỉ có thể thấy các phương thức và các biến mà phòng sạch có thể thấy (nhớ, mặc dù, giống như bất kỳ đối tượng nào, phòng sạch có thể thấy bất cứ thứ gì trong khung cảnh toàn cầu).

result = proc do 
    $SAFE = 4 
    clean_room.instance_eval do 
     binding 
    end.eval(command) 
    end.call 

in kết quả:

p result 
+0

Trông giống như những gì tôi đang tìm kiếm. Cảm ơn bạn. – nolk

+0

Chỉ cần tò mò - tại sao một 'instance_eval' ở đây thay vì chỉ gọi' clean_room.binding.eval (command) '? –

+1

@Matt, #binding là riêng tư, vì vậy bạn phải sử dụng một chút "foo" để có được nó. –

Các vấn đề liên quan