Ruby Sandbox 实现运行客户代码
碰到的问题是需要 运行客户自己编程的代码,同时要保证安全性,在碰到 system("rm")时,保证不抓狂
Ruby 自身是带有安全机制的,详细参看 Programming Ruby 2nd , 第25章 Ruby 安全
简略的说Ruby有四个安全级(详细参看Programming Ruby),等级4正是客户代码运行的理想的环境,不会污染到其他代码,也不会破坏系统,相当严格
示例代码如下:
def safely(code) sandbox = lambda do input = "something" $SAFE = 4 eval code end sandbox.call end code = File.new("file.rb").read result = safely(code) puts result
代码很简单
file.rb读入的code 是 "input * 2"
sandbox提供了code的运行环境 : input作为输入变量 , 提升了$SAFE等级
得到的输出是 "somethingsomething"
当我们在code中加入 system("rm") / system("del *") 时,得到以下错误
$SAFE 成功保护了我们的系统 , 当试图侵入源程序,也会得到错误
问题讨论:
1. $SAFE = 4 ,是个十分严格的规定,之后的代码不能侵入系统、不能修改"未污染"的对象等等,当安全级提升到4时,之后我们的程序代码怎么能正常运行而不受影响?$SAFE 的作用域 被绑定在了一个Proc内,仅在sandbox中有效,不对外面的代码产生安全级的影响。详细参看 : $SAFE is Proc-local(http://www.davidflanagan.com/2008/11/safe-is-proc-lo.html)
2. 考虑以下代码
def safely(code) temp_box = lambda do input = "something" eval code end sandbox = lambda do $SAFE = 4 temp_box.call end sandbox.call end code = File.new("file.rb").read result = safely(code) puts result当code中的代码是 system("shutdown")的时候,你很有可能看不到下面的内容(是很有可能,可以来测试你是不是有管理员的权限,当然也可以用 system("format")测试,未知结果)
temp_box 的运行环境 $SAFE 是默认值0,不受之前$SAFE的影响,一定要注意
3. 剩下待解决的问题就是:code中不能声明函数,Programming Ruby 2nd中这样描述:不能在未污染的类或模块内定义、重定义、删除或取消定义方法。需要寻找方法让code中能声明函数.
补充:关于问题3的讨论
1. 考虑以下代码
class Test def safely sandbox = lambda do self.class.taint $SAFE = 4 #code def something "success !!" end something #code end end sandbox.call end end result = Test.new.safely puts result
我们将在code域内成功定义方法,并得到输出 "success!!"
但是注意到 self.class.taint , 我们就看到整个Test类被污染了,something方法会渗透添加到 所有Test对象中,这违背了我们期望的沙盒原则
需要注意的是: def 中的def 是在类中添加实例方法
2. 对于以上问题的解决:
class Test def safely sandbox = lambda do self.taint $SAFE = 4 #code def self.something "success !!" end something #code end end sandbox.call end end result = Test.new.safely puts result
以上代码只将 self这一个实例 "污染" ,定义了单例方法 something,得到了成功的结果
作为代价,方法定义的时候必须要 self. , 这个很不人性化,需要考虑其他的解决方案