参考手册:Ruby 语言样式——安全模型
为了安全地运行CGI等程序,php?name=Ruby" onclick="tagshow(event)" class="t_tag">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级相同
禁止的操作
* 下列以受污染字符串为参数的操作
o Dir, IO, File、FileTest的类方法、方法
o 使用FileTest操作符、比较文件的更新时间
o 执行外部命令(system, exec, ``)
o eval (参考4级的说明)
o 加载顶层(若使用第二参数进行wrap则可以执行)
o require
o 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_object ruby 1.7 feature
* 改变环境变量
* srand
其他的安全级别相关信息
* 当$SAFE = 0时才执行require
* 若超过Level1的话,启动时会有下列不同
o 不把环境变量RUBYLIB添加到$:之中
o 不把当前目录添加到$:之中
o 不处理环境变量RUBYOPT
o 不能使用下列开关 -s -S -e -r -i -I -x (就算脚本被setgid, setuid也是如此)
o 不会从标准输入读入程序 (就算脚本被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" # 外部不受影响
扩展库中的应对
* 在扩展库中,有必要对对象的污染状态进行适当的传播。
* 改变全局状态或与外部联系之前,有必要检查安全级别。