在动态语言中可以完成许多“整洁”的事情,可以将它们隐藏在代码的某些部分中,这些部分对于另一个程序员或审计人员而言,对于给定代码段的功能而言并不立即显而易见。
考虑irb(交互式红宝石外壳)中的以下顺序:
irb(main):001:0> "bar".foo
NoMethodError: undefined method `foo' for "bar":String
from (irb):1
from /usr/bin/irb:12:in `<main>'
irb(main):002:0> class String
irb(main):003:1> def foo
irb(main):004:2> "foobar!"
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> "bar".foo
=> "foobar!"
在那里发生的事情是我试图foo
在String常量中调用该方法。这失败了。然后,我打开String类并定义了foo
return 方法"foobar!"
,然后对其进行了调用。这工作了。
这被称为开放类,每当我想到用红宝石编写具有任何安全性或完整性的代码时,都会感到噩梦。当然,它可以使您快速地完成一些整洁的事情……但是我可以做到,这样每当有人存储一个字符串时,它就会将其存储到文件中,或者通过网络发送。重新定义String的这一点可以放在代码中的任何位置。
许多其他动态语言也可以完成类似的操作。Perl具有Tie :: Scalar,可以在幕后改变给定标量的工作方式(这更明显,并且需要您可以看到的特定命令,但是从其他地方传入的标量可能是个问题)。如果您有权访问Perl Cookbook,请查阅配方13.15-使用领带创建魔术变量。
由于这些因素(以及其他一些因素通常是动态语言的一部分),因此无法对代码中的安全性进行静态分析的许多方法都行不通。 Perl和Undecidability证明了这种情况,甚至指出了语法突出显示这样的琐碎问题(whatever / 25 ; # / ; die "this dies!";
带来挑战,因为whatever
可以在运行时将其定义为接受参数或不接受参数,从而完全击败了语法突出显示器或静态分析器)。
Ruby可以访问定义了闭包的环境,因此在Ruby中可能会变得更加有趣(请参阅Joshua Ballanco的YouTube: RubyConf 2011中的Ruby合理化)。MouseTheLuckyDog在Ars Technica的评论中使我意识到了该视频。
考虑以下代码:
def mal(&block)
puts ">:)"
block.call
t = block.binding.eval('(self.methods - Object.methods).sample')
block.binding.eval <<-END
def #{t.to_s}
raise 'MWHWAHAW!'
end
END
end
class Foo
def bar
puts "bar"
end
def qux
mal do
puts "qux"
end
end
end
f = Foo.new
f.bar
f.qux
f.bar
f.qux
这段代码是完全可见的,但是该mal
方法可以在其他地方使用……当然,对于开放类,可以在其他地方重新定义该方法。
运行此代码:
〜/ $红宝石foo.rb
酒吧
> :)
x
酒吧
b.rb:20:in'qux':MWHWAHAW!(RuntimeError)
来自b.rb:30:in'
〜/ $红宝石foo.rb
酒吧
> :)
x
b.rb:20:in'bar':MWHWAHAW!(RuntimeError)
来自b.rb:29:in
在这段代码中,闭包能够访问该范围内该类中定义的所有方法和其他绑定。它选择了一个随机方法并重新定义它以引发异常。(请参阅Ruby中的Binding类以了解此对象可以访问的内容)
在此上下文中可以访问的变量,方法,自身值以及可能的迭代器块都将保留。
显示变量的重新定义的简短版本:
def mal(&block)
block.call
block.binding.eval('a = 43')
end
a = 42
puts a
mal do
puts 1
end
puts a
运行时会产生:
42
1个
43
这不仅仅是我上面提到的使静态分析变得不可能的开放类。上面展示的是,传递给其他地方的闭包带有定义所在的完整环境。这被称为一流环境(就像您可以传递函数一样,它们是一流函数,这是环境以及当时所有可用的绑定)。可以重新定义在闭包范围内定义的任何变量。
是好是坏,抱怨或不抱怨红宝石(在某些情况下人们希望能够获得一种方法的环境(请参阅Perl中的Safe)),“为什么在政府项目中限制使用红宝石,这是一个问题”确实在上面链接的视频中得到了回答。
鉴于:
- Ruby允许人们从任何闭包中提取环境
- Ruby捕获闭包范围内的所有绑定
- Ruby将所有绑定保持为实时且可变的
- Ruby具有新的绑定,而不是旧的绑定(而不是克隆环境或禁止重新绑定)
有了这四个设计选择的含义,就不可能知道任何代码的作用。
有关更多信息,请参见Abstract Heresies博客。特别的帖子是关于Scheme的辩论。(与SO相关:Scheme为什么不支持一流的环境?)
但是,随着时间的流逝,我开始意识到,一流的环境比我最初想象的要困难得多,功能也更少。在这一点上,我认为一流的环境充其量是无用的,而最坏的情况则是危险的。
我希望本节显示一流环境的危险方面,以及为什么会要求从提供的解决方案中删除Ruby。不仅Ruby是一种动态语言(如其他提及,答案,其他项目中允许使用其他动态语言),而且还有一些特定的问题使某些动态语言更加难以推理。