有几种方法可以从需要/加载该库的Ruby代码中访问库的源代码。在这些方法中,有些人直接读取库文件并解析它。其他人通过一些内置方法访问源代码,这些方法提供有关源的信息(例如抽象语法树)。在我无法直接读取文件内容的情况下(如前面的方式),访问源的唯一方法是访问提供信息的内置方法。通过重新定义这些方法来做其他事情,我将完全失去对源代码的访问权限。最小的方法集是什么,如果我将它们重新定义为其他方法,我将完全失去对外部文件库的源代码的访问权限?
<小时/> 重新解释问题
假设:
我必须通过在文件B中写入来重新定义(无效)或删除哪些(标准Ruby)方法,以使用户无法访问文件B中写入的源(通过用户可以编写的任何代码)在文件A)当我运行文件B?
有一些像sorcerer,pry这样的库可以提取它有权访问的方法的源代码。在纯Ruby中必须有一些原始命令,这些库依赖于这些命令使它们可以访问源代码。有什么方法可以使这种事情成为可能?
如果您不知道完整的答案,但知道某个特定的图书馆如何提取某些方法的来源,那么这仍然会有所帮助。
答案 0 :(得分:6)
TL; DR :仅限Ruby的解决方案只能使用source_location
,因此只需重新定义此项即可返回['/some/empty/file', 1]
之类的内容。对解释器的攻击不使用source_location
,但您可以阻止/白名单require
和朋友阻止使用C扩展。
首先,为了能够执行Ruby脚本,你必须能够阅读它......
但回到这个问题。我知道Sourcify除了在Proc和Method上使用一个名为source_location
的方法之外,不使用任何神秘的方法,它给出了文件名和行号是一个方法/ proc定义。我从经验中知道这种方法非常脆弱,需要编写某种解析器,有时只能在合法的情况下工作。所以,如果你在B中重新定义source_location
以返回/dev/null, line 0
之类的内容并让Sourcify抛出一个非Ruby源异常,那么Sourcify已经出局了。
从Pry的source开始,似乎Pry使用了相同的source_location
方法,所以两只一石二鸟。
现在,所有这些库都有另一种选择,即下拉到C并破解解释器以记录源代码。这几乎完美无瑕。但我们仍然可以通过一种非常简单的方式避免危险。有人可以在A中包含Pry方法源的所有代码。但是,如果不需要C库,则不能包含内联C / C扩展。因此,解决方案显而易见:重新定义require
和require_relative
以及load
要么不起作用,要么只允许某些库。这样,你可以阻止C黑客攻击。
在MRI上,除了source_location
之外,没有办法(来自Ruby代码)来做这件事。你去吧!
编辑:根据@banister,来自MRI 2.0+的内置binding_of_caller
方法可以取代源位置。也是这样。 ;)
警告:Ruby不是一个很好的语言。如果你可以对它们进行元编程,除非你处于不同的过程,否则它们可能会对你进行元编程。
答案 1 :(得分:0)
如果您使用John Mair(Pry的制造商)的精彩'method_source'宝石,这实际上非常容易: 该方法必须在Ruby(而不是C)中实现,并且必须从文件(而不是irb)加载。
以下是使用method_source在Rails控制台中显示方法源代码的示例:
$ rails console
> require 'method_source'
# the following prints out the method code for #lookup in the Rails I18n Backend:
> I18n::Backend::Simple.instance_method(:lookup).source.display
def lookup(locale, key, scope = [], options = {})
init_translations unless initialized?
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
keys.inject(translations) do |result, _key|
_key = _key.to_sym
return nil unless result.is_a?(Hash) && result.has_key?(_key)
result = result[_key]
result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
result
end
end
=> nil
显示的Ruby代码需要来自已经加载的文件, (显然它必须是可读的),它需要是原生的Ruby代码(这对编译/链接库不起作用)。
另见: