安全模型
為了安全地運行CGI等程序,Ruby設(shè)置了安全結(jié)構(gòu)。
Ruby的安全模型由“對象的污染”和“安全級別”構(gòu)成。
對象的污染
Ruby有時會認為對象“遭到了污染”,這主要有兩種用途。
第一,以不安全的輸入為基礎(chǔ)制成的對象就是“受污染”的對象,不能用作“危險操作”的參數(shù)。這主要是為了防止惡意數(shù)據(jù)導致程序作出一些意外的危險動作。
第二,可以使安全對象(未遭污染的對象)得到保護,免遭不安全對象的威脅。若安全級別為4,則對未受污染的對象進行操作時就會受到很多限制,這正體現(xiàn)了對于安全方面的考慮。
與對象的污染有關(guān)的方法
- Object#taint
-
污染對象
- Object#tainted?
-
若對象受到了污染就返回真
- Object#untaint
-
消除對象受到的污染
安全級別
每個線程都有特有的“安全級別”。安全級別越高,操作受到的限制也就越多。線程局部變量$SAFE標明了安全級別。
[ruby-list:37415]
$SAFE的相關(guān)規(guī)則
- 程序開始時$SAFE的值為0
- 各線程在生成時繼承父線程的$SAFE值
- 不能降低現(xiàn)有的$SAFE值
從原則上講,低安全等級時的限制也適用于高安全等級。例如,若某操作在1級就被禁止的話,在2級就更不可能通過了。
0級
默認的安全級別。
被污染對象
環(huán)境變量PATH比較特殊,只有當其值中含有危險路徑時才會受到污染。
這時所說的危險路徑是指,誰都可以變更或?qū)懭氲穆窂健母夸浧饘訉訖z查,若包含誰都可以更改的地方的話,該路徑就是危險的。
禁止的操作
1級
特指以安全程序處理不安全數(shù)據(jù)的情況。適合于用CGI等處理用戶的輸入。
被污染對象
禁止的操作
- 下列以受污染字符串為參數(shù)的操作
- Dir, IO, File、FileTest的類方法、方法
- 使用FileTest操作符、比較文件的更新時間
- 執(zhí)行外部命令(system, exec, ``)
- eval (參考4級的說明)
- 加載頂層(若使用第二參數(shù)進行wrap則可以執(zhí)行)
- require
- trap
- 執(zhí)行外部命令(只有當環(huán)境變量PATH中包含危險路徑時)
2級
被污染對象
禁止的操作
在1級限制的基礎(chǔ)上,以下操作也被禁止。
- 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
- 以被污染字符串為參數(shù)的load(即使被wrap也不行)
- syscall
- exit!
- trap
3級
所有生成的對象都被污染。適于為在4級狀態(tài)下運行程序提供環(huán)境。
被污染對象
禁止的操作
在2級限制的基礎(chǔ)上,以下操作也被禁止。
4級
執(zhí)行不安全程序時等級。
此時,3級時禁止的“受污染字符串的eval”卻被解禁。(這是因為用eval時,所有的危險操作都已經(jīng)被禁止了。)
被污染對象
禁止的操作
在3級限制(如上所述,不包括eval)的基礎(chǔ)上,以下操作也被禁止。
- Object#taint
- 改變頂層的定義(autoload, load, include)
- 對既存方法的再定義
- 改變Object類的定義
- 改變未被污染的類和模塊的定義或改變類變量
- 改變未被污染的對象的狀態(tài)
- 改變未被污染的全局變量
- 使用未被污染的IO及File的處理
- 輸出到IO
- 程序的終結(jié)(exit, abort)(且out of memory也不fatal)
- 對其他線程造成影響的Thread類的操作以及其他線程的Thread#[]
- ObjectSpace._id2ref
- ObjectSpace.each_object ruby 1.7 feature
- 改變環(huán)境變量
- srand
其他的安全級別相關(guān)信息
- 當$SAFE = 0時才執(zhí)行require
- 若超過Level1的話,啟動時會有下列不同
- 不把環(huán)境變量RUBYLIB添加到$:之中
- 不把當前目錄添加到$:之中
- 不處理環(huán)境變量RUBYOPT
- 不能使用下列開關(guān) -s -S -e -r -i -I -x (就算腳本被setgid, setuid也是如此)
- 不會從標準輸入讀入程序 (就算腳本被setgid, setuid也一樣)
- 被setuid, setgid的腳本將在超過$SAFE = 1的狀態(tài)下運行。
- 在3級以上的環(huán)境中生成的Proc將會記下該時刻的安全級別。若受污染的Proc對象被call的話,它將以記憶的安全級別來運行。
- 若受污染的Method對象被call的話,將以4級狀態(tài)運行。
- 若將受污染的字符串指定為trap/trace_var的第二參數(shù)時,將以4級狀態(tài)運行ruby 1.7 feature:在 version 1.7中,若將受污染的字符串指定為第二參數(shù)而運行trap/trace_var的話,馬上就會引發(fā)異常SecurityError。
- 超過4級的話,即使out of memory也不會fatal。
- 根據(jù)您安裝情況的不同,F(xiàn)ixnum Symbol true false nil可能不會被污染。但請注意Bignum Float可能會受到污染。
實例
$SAFE級別一旦升高就不能調(diào)低了。如下所示,可以使用線程將程序的一部分置入高安全級別狀態(tài)下運行。
例:
def safe(level)
result = nil
Thread.start {
$SAFE = level
result = yield
}.join
result
end
safe(4) { puts "hello" } # 因為是$SAFE所以例外
puts "world" # 外部不受影響
擴展庫中的應(yīng)對
- 在擴展庫中,有必要對對象的污染狀態(tài)進行適當?shù)膫鞑ァ?
- 改變?nèi)譅顟B(tài)或與外部聯(lián)系之前,有必要檢查安全級別。
[ruby-list:37407]