如何在Ruby中进行防御性编程?

时间:2020-03-05 18:44:23  来源:igfitidea点击:

这是问题的一个完美示例:分类器gem破坏了Rails。

原始问题:

作为安全专家,与我有关的一件事是Ruby与Java的程序包隐私没有相似之处。也就是说,这不是有效的Ruby:

public module Foo
  public module Bar
    # factory method for new Bar implementations
    def self.new(...)
      SimpleBarImplementation.new(...)
    end
    def baz
      raise NotImplementedError.new('Implementing Classes MUST redefine #baz')
    end
  end

  private class SimpleBarImplementation
    include Bar
    def baz
      ...
    end
  end
end

能够阻止Foo :: BarImpl的猴子修补真是太好了。这样,依赖图书馆的人就会知道没有人搞砸它。想象一下,如果有人改变了我们对MD5或者SHA1的实现!我可以在这些类上调用freeze,但是我必须逐个类地进行操作,如果我对加载顺序不是很谨慎的话,其他脚本可能会在完成应用程序安全之前对其进行修改。

Java提供了许多其他用于防御性编程的工具,其中许多在Ruby中是不可能的。 (请参阅乔什·布洛赫(Josh Bloch)的书,以获得一个不错的清单。)这真的是一个问题吗?我是否应该停止抱怨并将Ruby用于轻量级产品,而不希望有"企业就绪"的解决方案?

(不,默认情况下,Ruby中不冻结核心类。请参见下文:)

require 'md5'
# => true
MD5.frozen?
# => false

解决方案

回答

我想Ruby具有一个比它更有价值的功能,它是一个安全问题。小鸭也一样。
例如。我可以将自己的方法添加到Ruby String类,而不是扩展或者包装它。

回答

我不认为这是一个问题。

是的,神话般的"某人"可以用一些不安全的方法代替MD5的实现。但是为了做到这一点,神话般的人实际上必须能够将他的代码带入Ruby进程。如果他能做到这一点,那么他大概也可以将其代码注入Java进程中,例如重写MD5操作的字节码。或者只是截取按键,而根本不去摆弄密码。

一个典型的问题是:我正在编写这个很棒的库,应该这样使用:

require 'awesome'
# Do something awesome.

但是,如果有人这样使用它,该怎么办:

require 'evil_cracker_lib_from_russian_pr0n_site'
# Overrides crypto functions and sends all data to mafia
require 'awesome'
# Now everything is insecure because awesome lib uses 
# cracker lib instead of builtin

简单的解决方案是:不要这样做!告知用户,他们不应运行在安全性至关重要的应用程序中从晦涩的来源下载的不受信任的代码。如果这样做,他们可能应该得到。

回到Java示例:的确,在Java中,我们可以将加密代码设为"私有"和"最终",而不能做到。但是,仍然有人可以替换加密实现!实际上,实际上有人这样做了:许多开源Java实现都使用OpenSSL来实现其加密例程。而且,我们可能已经知道,Debian发行了一个损坏的,不安全的OpenSSL版本已有多年了。因此,过去几年在Debian上运行的所有Java程序实际上都使用不安全的密码运行!

回答

Java provides lots of other tools for defensive programming

最初,我以为我们是在谈论普通的防御性编程,
其中的想法是保护程序(或者其子集或者单个功能)免受无效数据输入的侵害。
真是太好了,我鼓励每个人都去读那篇文章。

但是,我们似乎实际上是在谈论"保护其他程序员的代码"。

在我看来,这是一个毫无意义的目标,因为无论我们做什么,恶意程序员始终可以在调试器下运行程序,或者使用dll注入或者许多其他技术。

如果我们只是想保护自己的代码免受不称职的同事的侵害,那就太可笑了。教育同事,或者获得更好的同事。

无论如何,如果我们非常关心此类事情,那么ruby并不是编程语言。 Monkeypatching在设计上就在其中,并且不允许它与功能的整个要点相抵触。

回答

"教育同事,或者成为更好的同事"对于小型软件初创公司非常有用,对于谷歌和亚马逊等大手笔也非常有用。认为每个低收入的开发人员都在一个小城市的医生办公室签约了一些小型病历应用程序,这是荒谬的。

我并不是说我们应该为最低的公分制而构建,但是我们必须现实,那里有许多中等水平的程序员,他们会引入任何可以完成工作的库,而不关注安全性。他们如何注意安全性?也许上了算法和数据结构课。也许他们参加了编译器课程。他们几乎可以肯定没有参加加密协议课程。他们肯定还没有读过Schneier或者其他几乎任何人,他们甚至不得不恳求甚至非常优秀的程序员在构建软件时也要考虑安全性。

我对此并不担心:

require 'evil_cracker_lib_from_russian_pr0n_site'
require 'awesome'

我担心awesome需要foobarfazbot,以及foobar需要has_gumption,并且...最终,其中两个冲突以某种模糊的方式消除了重要的安全性。

一个重要的安全原则是"纵深防御"-添加这些额外的安全层可避免意外射击自己的脚。他们无法完全防止这种情况;没有办法。但是他们有帮助。

回答

看看Garry Dolley的Immutable。

我们可以防止重新定义单个方法。

回答

如果我们喜欢猴子修补,则可以使用Immutable模块(或者类似功能之一)。

一成不变的

回答

我们可以看一下为什么Lucky Stiff的" Sandbox"项目,如果我们担心潜在地运行不安全的代码,可以使用该项目。
http://code.whytheluckystiff.net/sandbox/

一个示例(在线TicTacToe):
http://www.elctech.com/blog/safely-exposed-your-app-to-a-ruby-sandbox

回答

如果有人用猴子修补了对象或者模块,则需要查看2种情况:他添加了一个新方法。如果他是唯一添加此方法的人(很有可能),则不会出现任何问题。如果他不是唯一的一个,那么我们需要查看两种方法是否都相同,并告知库开发人员这个严重的问题。

如果他们更改了方法,则应该开始研究更改方法的原因。他们是由于某些极端情况而更改了它,还是实际上已修复了错误?特别是在后一种情况下,monkeypatch是一件很重要的事情,因为它在许多地方修复了一个错误。

除此之外,我们正在使用一种非常动态的语言,并假定程序员以理智的方式使用这种自由。消除此假设的唯一方法是不使用动态语言。

回答

拉根瓦尔德(Raganwald)最近对此发表了一篇文章。最后,他构建了以下内容:

class Module
  def anonymous_module(&block)
   self.send :include, Module.new(&block)
  end
end

class Acronym
  anonymous_module do
    fu = lambda { 'fu' }
    bar = lambda { 'bar' }
    define_method :fubar do
      fu.call + bar.call
    end
  end
end

这将fubar作为首字母缩略词s上的公共方法公开,但将内部胆量(fubar)保持私有状态,并从外部隐藏帮助程序模块。