当前位置: 首页 > 文档资料 > Ruby 参考手册 >

安全模型

优质
小牛编辑
133浏览
2023-12-01

为了安全地运行CGI等程序,Ruby设置了安全结构。

Ruby的安全模型由“对象的污染”和“安全级别”构成。

对象的污染

Ruby有时会认为对象“遭到了污染”,这主要有两种用途。

第一,以不安全的输入为基础制成的对象就是“受污染”的对象,不能用作“危险操作”的参数。这主要是为了防止恶意数据导致程序作出一些意外的危险动作。

第二,可以使安全对象(未遭污染的对象)得到保护,免遭不安全对象的威胁。若安全级别为4,则对未受污染的对象进行操作时就会受到很多限制,这正体现了对于安全方面的考虑。

与对象的污染有关的方法

Object#taint

污染对象

Object#tainted?

若对象受到了污染就返回真

Object#untaint

消除对象受到的污染

安全级别

每个线程都有特有的“安全级别”。安全级别越高,操作受到的限制也就越多。线程局部变量$SAFE标明了安全级别。

[ruby-list:37415]

$SAFE的相关规则

  • 程序开始时$SAFE的值为0
  • 各线程在生成时继承父线程的$SAFE
  • 不能降低现有的$SAFE

从原则上讲,低安全等级时的限制也适用于高安全等级。例如,若某操作在1级就被禁止的话,在2级就更不可能通过了。

0级

默认的安全级别。

被污染对象

  • 可从IO、环境变量或命令行参数(ARGV)中获得的字符串

    (只有环境变量PATH例外)

环境变量PATH比较特殊,只有当其值中含有危险路径时才会受到污染。

这时所说的危险路径是指,谁都可以变更或写入的路径。从根目录起层层检查,若包含谁都可以更改的地方的话,该路径就是危险的。

禁止的操作

  • 没有

1级

特指以安全程序处理不安全数据的情况。适合于用CGI等处理用户的输入。

被污染对象

  • 与0级相同

禁止的操作

  • 下列以受污染字符串为参数的操作
    • Dir, IO, File、FileTest的类方法、方法
    • 使用FileTest操作符、比较文件的更新时间
    • 执行外部命令(system, exec, ``)
    • eval (参考4级的说明)
    • 加载顶层(若使用第二参数进行wrap则可以执行)
    • require
    • trap
  • 执行外部命令(只有当环境变量PATH中包含危险路径时)

2级

被污染对象

  • 与1级相同

禁止的操作

在1级限制的基础上,以下操作也被禁止。

  • Dir.chdir Dir.chroot Dir.mkdir Dir.rmdir
  • File.chown File.chmod File.umask File.truncate File#lstat File#chmod File#chown File#delete File#unlink File#truncate File#flock 以及FileTest模块的方法
  • IO#ioctl, IO#fcntl
  • Process.fork Process.setpgid Process.setsid Process.setpriority Process.egid= Process.kill
  • 使用危险路径load
  • 以被污染字符串为参数的load(即使被wrap也不行)
  • syscall
  • exit!
  • trap

3级

所有生成的对象都被污染。适于为在4级状态下运行程序提供环境。

被污染对象

  • 所有生成的对象

禁止的操作

在2级限制的基础上,以下操作也被禁止。

  • Object#untaint

4级

执行不安全程序时等级。

此时,3级时禁止的“受污染字符串的eval”却被解禁。(这是因为用eval时,所有的危险操作都已经被禁止了。)

被污染对象

  • 与3级相同。

禁止的操作

在3级限制(如上所述,不包括eval)的基础上,以下操作也被禁止。

  • Object#taint
  • 改变顶层的定义(autoload, load, include)
  • 对既存方法的再定义
  • 改变Object类的定义
  • 改变未被污染的类和模块的定义或改变类变量
  • 改变未被污染的对象的状态
  • 改变未被污染的全局变量
  • 使用未被污染的IO及File的处理
  • 输出到IO
  • 程序的终结(exit, abort)(且out of memory也不fatal)
  • 对其他线程造成影响的Thread类的操作以及其他线程的Thread#[]
  • ObjectSpace._id2ref
  • ObjectSpace.each_objectruby 1.7 feature
  • 改变环境变量
  • srand

其他的安全级别相关信息

  • 当$SAFE = 0时才执行require
  • 若超过Level1的话,启动时会有下列不同
    • 不把环境变量RUBYLIB添加到$:之中
    • 不把当前目录添加到$:之中
    • 不处理环境变量RUBYOPT
    • 不能使用下列开关 -s -S -e -r -i -I -x (就算脚本被setgid, setuid也是如此)
    • 不会从标准输入读入程序 (就算脚本被setgid, setuid也一样)
  • 被setuid, setgid的脚本将在超过$SAFE = 1的状态下运行。
  • 在3级以上的环境中生成的Proc将会记下该时刻的安全级别。若受污染的Proc对象被call的话,它将以记忆的安全级别来运行。
  • 若受污染的Method对象被call的话,将以4级状态运行。
  • 若将受污染的字符串指定为trap/trace_var的第二参数时,将以4级状态运行ruby 1.7 feature:在 version 1.7中,若将受污染的字符串指定为第二参数而运行trap/trace_var的话,马上就会引发异常SecurityError
  • 超过4级的话,即使out of memory也不会fatal
  • 根据您安装情况的不同,Fixnum Symbol true false nil可能不会被污染。但请注意Bignum Float可能会受到污染。

实例

$SAFE级别一旦升高就不能调低了。如下所示,可以使用线程将程序的一部分置入高安全级别状态下运行。

例:

def safe(level)
  result = nil
  Thread.start {
    $SAFE = level
    result = yield
  }.join
  result
end
safe(4) { puts "hello" }    # 因为是$SAFE所以例外
puts "world"                # 外部不受影响

扩展库中的应对

  • 在扩展库中,有必要对对象的污染状态进行适当的传播。
  • 改变全局状态或与外部联系之前,有必要检查安全级别。

[ruby-list:37407]