Ruby Security
Ruby 安全
Ruby 编程语言庞大而复杂,新手和经验丰富的 Rubyists 经常遇到许多安全缺陷。
本文档旨在讨论这些缺陷,并在适用的情况下提供更安全的替代方案。
请查看公共已知CVE的完整列表以及如何正确报告安全漏洞,网址为:www.ruby-lang.org/en/security/ 日文版本位于:www.ruby-lang.org/ja/security /
应通过电子邮件将安全漏洞报告给 security@ruby-lang.org(PGP 公钥),这是一个私人邮件列表。报告的问题将在修复后发布。
$SAFE
Ruby 提供了一种机制来限制 Ruby 代码以$SAFE
变量的形式执行哪些操作。
但是,$SAFE
不提供执行不可信代码的安全环境。
如果您需要执行不受信任的代码,则应使用操作系统级别的沙盒机制。在Linux上,ptrace或LXC可用于沙盒可能的恶意代码。每个主要操作系统都有其他类似的机制。
Marshal.load
Ruby的Marshal
模块提供了用于序列化和反序列化二进制数据格式的 Ruby 对象树的方法。
切勿使用Marshal.load
反序列化不可信或用户提供的数据。因为Marshal
可以反序列化几乎所有的 Ruby 对象,并且可以完全控制实例变量,所以可以在反序列化之后立即构造一个执行代码的恶意负载。
如果您需要反序列化不可信数据,则应该使用 JSON,因为它只能返回“原始”类型,如字符串,数组,哈希,数字和零。如果你需要反序列化其他类,你应该手动处理。切勿反序列化为用户指定的类。
YAML
YAML 是一种流行的人类可读数据序列化格式,被许多 Ruby 程序用于 Ruby 对象树的配置和数据库持久性。
类似的Marshal
,它可以反序列化成任意的 Ruby 类。例如,以下 YAML 数据将ERB
在反序列化时创建一个对象:
!ruby/object:ERB
src: puts `uname`
因此,适用于元帅的许多安全考虑事项也适用于 YAML。不要使用 YAML 来反序列化不可信数据。
符号
符号通常被视为简单字符串的语法糖,但它们扮演着非常关键的角色。MRI Ruby 实现在方法,变量和常量名称内部使用符号。其原因是符号只是名称附加的整数,所以它们在哈希表中查找速度更快。
从版本2.2开始,大多数符号可以被垃圾收集; 这些被称为平凡的符号。你创建的大多数符号(例如通过调用to_sym
)都是致命的。
另一方面,不朽的符号永远不会被垃圾收集。修改代码时会创建它们:
- 定义一个方法(例如
define_method
),
- 设置一个实例变量(例如with
instance_variable_set
),
- 创建一个变量或常量(例如与
const_set
)
尚未更新且仍在呼叫的 C 扩展SYM2ID
将创建不朽的符号。2.2.0中的错误:send
+ __ send
__ +也创建了不朽的符号,并且调用具有关键字参数的方法也可以创建一些。
不要从用户输入中创建不朽的符号。否则,这将允许用户通过使用唯一字符串填充对应用程序的拒绝服务攻击,这会导致内存无限增长,直到 Ruby 进程死亡或导致系统减速停止。
虽然它可能不是一个好主意,与用户输入,曾经是脆弱的,如方法调用这些to_sym
,respond_to?
,method
,instance_variable_get
,const_get
,等不再是一个威胁。
常用表达
与其他语言相比,Ruby 的正则表达式语法有一些细微差别。在R uby中,^
和$
锚不是指字符串,而开始和结束的开始和结束行
。
这意味着如果您使用正则表达式/^[a-z]+$/
来将字符串限制为仅字母,攻击者可以通过传递包含字母,换行符和任意字符串的字符串来绕过此检查。
如果你想匹配 Ruby 中整个字符串的开头和结尾,使用锚\A
和\z
。
eval
切勿将不可信或用户控制的输入传递给eval
。
除非你实现像 REPL irb
或者pry
,eval
是几乎可以肯定不是你想要的。不要试图在传递给用户输入之前过滤用户输入eval
- 这种方法充满了危险,并且很可能会将您的应用程序打开到严重的远程代码执行漏洞。
send
Ruby 中的“全局函数”(puts
,exit
等)实际上是私有的实例方法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...")
DRb
由于 DRb 允许远程客户端调用任意方法,因此不适合暴露给不受信任的客户端。
使用 DRb 时,尽量避免将其暴露在网络上。如果这不可行并且您需要将 DRb 公开给世界,则必须
使用相应的安全策略进行配置DRb::ACL
。