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

5
这是一个很好的例子,说明了问题:分类器宝石破坏了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个回答

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中,您可以将加密代码设置为“private”、“final”等。然而,有人仍然可以替换您的加密实现!事实上,有人确实这样做了:许多开源Java实现使用OpenSSL来实现它们的加密例程。而且,正如您可能知道的那样,Debian多年来一直使用有缺陷、不安全的OpenSSL版本。因此,过去几年在Debian上运行的所有Java程序实际上都使用了不安全的加密!

4

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

起初我以为你在谈论普通的防御性编程,其中的想法是保护程序(或其子集或单个功能)免受无效数据输入的影响。
这是一件好事,我鼓励每个人去阅读那篇文章。

然而,看起来你实际上是在谈论“保护你的代码免受其他程序员的攻击”。

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

如果你只是想保护自己的代码免受无能的同事的攻击,那么这是荒谬的。 教育你的同事,或找到更好的同事。

无论如何,如果这些事情对你非常重要,Ruby不是适合你的编程语言。 Monkeypatching是有设计意图的,禁止它与该功能的整个目的相悖。


1
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方法,但保持内部实现(fubar)私有并隐藏外部视图中的帮助器模块。

1
“教育你的同事,或者招募更好的同事”对于小型软件创业公司非常有效,对于像Google和Amazon这样的大公司也非常有效。但认为每个低级开发人员都能做到这一点是荒谬的,他们可能只是为某个小医生办公室的医疗图表应用程序承包的开发人员。
我并不是说我们应该为最低公共分母而建立,但我们必须现实地看待事情,因为有很多平庸的程序员会使用任何可以完成工作的库,而不关注安全性。他们怎么可能关注安全呢?也许他们上过算法和数据结构课程,也许他们上过编译器课程,但他们几乎肯定没有上过加密协议课程。他们肯定没有读过Schneier或其他一些实际上必须“恳求和请求”即使是非常优秀的程序员在构建软件时考虑安全性的人的著作。
我不担心这个问题:
require 'evil_cracker_lib_from_russian_pr0n_site'
require 'awesome'

我担心 awesome 需要 foobarfazbot,而 foobar 又需要 has_gumption,最终这些依赖关系中有两个会以某种不明显的方式产生冲突,从而破坏一个重要的安全方面。

“深度防御”是一项重要的安全原则——增加这些额外的安全层可以帮助您避免意外自毁。它们无法完全预防;没有什么能够做到。但它们有所帮助。


1

看看 Garry Dolley 的 Immutable

你可以防止单个方法的重新定义。


虽然不完全相同,但它的效果还是相当不错的。只需在定义类之后将其冻结即可。但是仍然会出现命名空间混乱的问题... - James A. Rosen

1
如果您担心猴子补丁问题,可以使用Immutable模块(或类似功能的模块)。

Immutable


1

1

我猜Ruby有这样一个特性 - 更重要的是它被视为安全问题。还有鸭子类型。
例如,我可以向Ruby字符串类添加自己的方法,而不是扩展或包装它。


0
如果有人对一个对象或模块进行了猴子补丁,那么你需要考虑两种情况:他添加了一个新方法。如果他是唯一添加此方法的人(这很可能),那么就没有问题。如果不是唯一的,则需要查看两个方法是否执行相同的操作,并告知库开发人员此严重问题。
如果他们更改了一个方法,你应该开始研究为什么要更改该方法。他们更改它是因为某些边缘情况行为还是实际修复了错误?特别是在后一种情况下,猴子补丁是一个好东西,因为它可以在许多地方修复错误。
除此之外,你正在使用一种非常动态的语言,并假设程序员以明智的方式使用这种自由。消除这种假设的唯一方法是不使用动态语言。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接