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

时间:2008-08-28 14:07:15

标签: ruby security defensive-programming

以下是问题的完美示例:Classifier gem breaks 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

9 个答案:

答案 0 :(得分:9)

我不认为这是一个问题。

是的,神秘的“某人”可以用不安全的东西取代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中,您可以创建加密代码privatefinal,而不是。但是,有人可以仍然替换您的加密实现!事实上,有人确实做过:许多开源Java实现使用OpenSSL来实现其加密例程。而且,正如您可能知道的那样,Debian多年来一直在发布一个破碎,不安全的OpenSSL版本。因此,过去几年在Debian上运行的所有Java程序实际上已经运行了不安全的加密!

答案 1 :(得分:4)

  

Java为防御性编程提供了许多其他工具

最初我以为你在谈论normal defensive programming, 其中的想法是从无效数据输入中保护程序(或其子集或单个函数) 这是一件好事,我鼓励大家去阅读那篇文章。

但是,你似乎在谈论“从其他程序员那里捍卫你的代码”。

在我看来,这是一个完全没有意义的目标,因为无论你做什么,恶意程序员总是可以在调试器下运行你的程序,或使用dll injection或任何其他技术。

如果你只是想保护你的代码免受不称职的同事的影响,那就太荒谬了。 教育你的同事,或者让更好的同事。

无论如何,如果你非常担心这些事情,ruby不是你的编程语言。 Monkeypatching在设计中存在,并且不允许它违背特征的整个点。

答案 2 :(得分:1)

我认为Ruby有一个功能 - 更重要的是它是一个安全问题。也是Ducktyping。
例如。我可以将自己的方法添加到Ruby String类中,而不是扩展或包装它。

答案 3 :(得分:1)

“教育你的同事,或者让更好的同事”对于一家小型软件创业公司来说非常有用,而且对谷歌和亚马逊这样的大枪来说非常有用。认为每个低级开发人员都在一个小城市的医生办公室收取一些小型医疗图表申请,这是荒谬的。

我并不是说我们应该为最低的共同点而建立,但我们必须现实地认为,那里有很多平庸的程序员可以吸引任何能够完成工作的库,不注意安全。 他们如何关注安全性?也许拿了算法和数据结构类。也许他们参加了一个编译器课程。他们几乎肯定没有采用加密协议类。他们肯定没有读过Schneier或其他任何人,他们几乎不得不与非常优秀的程序员一起讨论并恳求在构建软件时考虑安全性。

我并不担心这个:

require 'evil_cracker_lib_from_russian_pr0n_site'
require 'awesome'

我担心awesome需要foobarfazbotfoobar需要has_gumption,并且...最终有两个冲突晦涩的方式,揭示了一个重要的安全方面。

一个重要的安全原则是“纵深防御” - 添加这些额外的安全层可以帮助您不小心拍摄自己的脚。他们不能完全阻止它;什么都没有。但他们有所帮助。

答案 4 :(得分:1)

查看Garry Dolley的Immutable

您可以阻止重新定义单个方法。

答案 5 :(得分:1)

如果猴子补丁是你的浓缩,你可以使用Immutable模块(或类似功能之一)。

Immutable

答案 6 :(得分:1)

你可以看看为什么Lucky Stiff的“Sandbox”项目,如果你担心可能会运行不安全的代码,你可以使用它。 http://code.whytheluckystiff.net/sandbox/

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

答案 7 :(得分:1)

Raganwald对此有一个recent post。最后,他构建了以下内容:

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公开为Acronym上的公共方法,但保持内部内容(fubar)私有,并从外部视图隐藏帮助程序模块。< / p>

答案 8 :(得分:0)

如果某人对某个对象或模块进行了monkeypatched,那么你需要查看两种情况:他添加了一个新方法。如果他是唯一一个添加这个meyhod(很可能)的人,那么就不会出现问题。如果他不是唯一一个,你需要看看两种方法是否相同,并告诉图书馆开发人员这个严重的问题。

如果他们改变方法,你应该开始研究方法改变的原因。他们是否因为某些边缘案例行为而改变了它,或者他们是否真的修复了错误?特别是在后一种情况下,monkeypatch是一个神的东西,因为它修复了很多地方的错误。

除此之外,你使用的是一种非常动态的语言,假设程序员以理智的方式使用这种自由。删除此假设的唯一方法是不使用动态语言。