Ruby 安全

Ruby 编程语言庞大而复杂,许多安全隐患经常让新手和经验丰富的 Rubyist 都感到困扰。

本文档旨在讨论其中许多隐患,并在适用时提供更安全的选择。

请查看公开已知的 CVE 列表以及如何正确报告安全漏洞:www.ruby-lang.org/en/security/ 日文版在此:www.ruby-lang.org/en/security/

安全漏洞应通过电子邮件发送至 security@ruby-lang.orgPGP 公钥),这是一个私有邮件列表。报告的问题将在修复后发布。

Marshal.load

Ruby 的 Marshal 模块提供了将 Ruby 对象树序列化和反序列化为二进制数据格式的方法。

切勿使用 Marshal.load 反序列化不受信任或用户提供的数据。因为 Marshal 可以反序列化几乎任何 Ruby 对象并完全控制实例变量,所以可以构造一个恶意负载,在反序列化后不久执行代码。

如果您需要反序列化不受信任的数据,应该使用 JSON,因为它只能返回“原始”类型,如字符串、数组、哈希、数字和 nil。如果您需要反序列化其他类,应该手动处理。切勿反序列化到用户指定的类。

YAML

YAML 是一种流行的、人类可读的数据序列化格式,被许多 Ruby 程序用于配置和 Ruby 对象树的数据库持久化。

Marshal 类似,它能够反序列化为任意 Ruby 类。例如,以下 YAML 数据将使用 unsafe_load 方法在反序列化时创建一个 ERB 对象。

!ruby/object:ERB
src: puts `uname`

因此,许多适用于 Marshal 的安全注意事项也适用于 YAML。请勿使用 YAML 反序列化不受信任的数据。

符号 (Symbols)

符号通常被视为简单字符串的语法糖,但它们扮演着更关键的角色。MRI Ruby 实现内部使用符号来表示方法、变量和常量的名称。这是因为符号本质上是附有名称的整数,因此在哈希表中查找速度更快。

大多数符号都可以被垃圾回收;这些被称为易失性符号。您创建的大多数符号(例如通过调用 to_sym)都是易失性的。

另一方面,非易失性符号将永远不会被垃圾回收。它们是在修改代码时创建的

尚未更新且仍调用 SYM2ID 的 C 扩展将创建非易失性符号。

不要从用户输入中创建非易失性符号。否则,这会让用户通过向应用程序发送大量唯一字符串来发起拒绝服务攻击,导致内存无限增长,直到 Ruby 进程被终止或导致系统缓慢停止。

虽然不建议使用用户输入调用这些方法,但曾经容易受到攻击的方法,如 to_symrespond_to?methodinstance_variable_getconst_get 等,已不再构成威胁。

正则表达式

Ruby 的正则表达式语法与其他语言相比存在一些细微的差异。在 Ruby 中,^$ 锚点不指向字符串的开头和结尾,而是指向**行**的开头和结尾。

这意味着,如果您使用像 /^[a-z]+$/ 这样的正则表达式来限制字符串只包含字母,攻击者可以通过传递一个包含字母、换行符,然后是任意字符串的字符串来绕过此检查。

如果您想在 Ruby 中匹配整个字符串的开头和结尾,请使用 \A\z 锚点。

eval

切勿将不受信任或用户控制的输入传递给 eval

除非您正在实现类似 irbpry 的 REPL,否则 eval 几乎肯定不是您想要的。请勿尝试在将用户输入传递给 eval 之前对其进行过滤——这种方法充满了危险,很可能会使您的应用程序暴露于严重的远程代码执行漏洞。

send

Ruby 中的“全局函数”(putsexit 等)实际上是 Object 上的私有实例方法。这意味着可以使用 send 调用这些方法,即使 send 的调用有显式的接收者。

例如,以下代码片段会将“Hello world”写入终端

1.send(:puts, "Hello world")

您绝不应将用户提供的输入作为第一个参数调用 send。这样做可能会引入拒绝服务漏洞。

foo.send(params[:bar]) # params[:bar] is "exit!"

如果攻击者可以控制 send 的前两个参数,则可能导致远程代码执行。

# params is { :a => "eval", :b => "...ruby code to be executed..." }
foo.send(params[:a], params[:b])

根据用户输入分派方法调用时,请仔细验证方法名称。如果可能,请将其与安全方法名称列表进行核对。

请注意,使用 public_send 也很危险,因为 send 本身是公共的。

1.public_send("send", "eval", "...ruby code to be executed...")