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