2012-11-30 4 views
54

Es gibt mehrere Fragen auf, wie eval(parse(...))Was genau sind die Gefahren von eval (parse (...))?

zu vermeiden, mit dem der Fragen Funken:

  • Warum sollte Insbesondere eval(parse()) vermieden werden?
  • Und am wichtigsten, Was sind die Gefahren?
    • Gibt es irgendwelche gefährlichen, wenn der Code nicht in der Produktion verwendet wird? (Ich denke, jede Gefahr von unbeabsichtigten Ergebnissen immer wieder. Klar, wenn Sie nicht vorsichtig sind, was Sie Parsen, werden Sie Probleme haben. Aber ist das nicht mehr gefährlich als mit get() nachlässig zu sein?)
+7

Ich weiß nicht, ob ich Zeit haben werde, ein gutes Beispiel zu schreiben/eine richtige Antwort zu schreiben, aber meine Kopfschmerzen in dieser Richtung hatten mehr mit 'eval()' als mit 'eval (parse ()) '. Das Problem mit 'eval()' ist, dass es versucht, einige grundlegende, aber oft schwer verständliche Regeln zu den Frames zu befolgen, in denen der Ausdruck ausgewertet wird, und diese können Sie häufig bei komplexeren Code-Konstrukten beißen als der Autor ursprünglich dachte ... http://obeautifulcode.com/R/How-R-Searches-And-Finds-Stuff/ –

+0

@BenBolker, scheint diese Frage immer noch viele Suchergebnisse zu treffen. Wenn Sie Ihren Kommentar weiter oben erläutern möchten, würde ich gerne mehr von Ihren Gedanken hören, da ich mir sicher bin, dass andere dies auch tun würden. –

+0

@Ricardo Saporta, Sie scheinen es zu genießen, 'data.table' zu ​​verwenden. Interessanterweise stieß ich auf einen [post] (https://stackoverflow.com/a/10676138/5193830), wo erklärt wird, dass 'eval' in einigen Fällen schneller als' get' sein kann mit 'data.table' – Valentin

Antwort

38

Die meisten Argumente gegen eval(parse(...)) entstehen nicht wegen Sicherheitsbedenken verwenden können Schließlich werden keine Behauptungen aufgestellt, dass R eine sichere Schnittstelle zum Internet ist, sondern weil der Code im Allgemeinen Dinge erledigt, die mit weniger obskuren Methoden, dh Methoden, die sowohl schneller als auch menschlicher analysierbar sind, durchgeführt werden können .Die R-Sprache sollte auf hoher Ebene sein, also ist die Präferenz des cognoscenti (und ich betrachte mich selbst in dieser Gruppe nicht) einen Code, der sowohl kompakt als auch ausdrucksstark ist.

Also die Gefahr ist, dass eval(parse(..)) ist eine Backdoor-Methode, um Mangel an Wissen zu umgehen und die Hoffnung auf die Aufhebung dieser Barriere ist, dass die Menschen ihre Verwendung der R-Sprache zu verbessern. Die Tür bleibt offen, aber die Hoffnung besteht darin, andere Funktionen expressiver zu nutzen. Carl Witthoft's question earlier today illustriert nicht zu wissen, dass die get Funktion verfügbar war, und die question he linked to ausgesetzt ein Mangel an Verständnis dafür, wie die [[-Funktion verhielt (und wie $ war mehr als [[ begrenzt). In beiden Fällen könnte eine eval(parse(..)) Lösung konstruiert werden, aber sie war klirriger und weniger klar als die Alternative.

7

In einigen Programmiersprachen ist eval() eine Funktion, die eine Zeichenfolge auswertet, als wäre es ein Ausdruck und gibt ein Ergebnis zurück; in anderen führt es mehrere Codezeilen aus, als wären sie statt der Zeile einschließlich der Eval enthalten. Die Eingabe für eval ist nicht unbedingt eine Zeichenfolge; in Sprachen, die syntaktische Abstraktionen (wie Lisp) unterstützen, wird evals Eingabe aus abstrakten syntaktischen Formen bestehen. http://en.wikipedia.org/wiki/Eval

Es gibt alle Arten von Exploits, die ein Vorteil, wenn eval unsachgemäß verwendet wird dauern kann.

Ein Angreifer ein Programm mit dem String „session.update (authentifizierte = True)“ liefern könnte als Daten, die die Sitzung Wörterbuch aktualisieren würde eine authentifizierte Schlüssel gesetzt wahr sein. Um dies zu beheben, müssen alle Daten, die mit eval verwendet werden, maskiert werden, oder es muss ohne Zugriff auf potenziell schädliche Funktionen ausgeführt werden. http://en.wikipedia.org/wiki/Eval

Mit anderen Worten, ist die größte Gefahr von eval() das Potenzial für Code-Injektion in Ihre Anwendung. Die Verwendung von eval() kann je nach Verwendung auch zu Leistungsproblemen in einigen Sprachen führen.

Insbesondere in R, ist es wahrscheinlich, weil Sie get() anstelle von eval(parse()) und Ihre Ergebnisse werden die gleichen sein, ohne zu müssen zu eval()

+10

konkreten Beispielen von Strings, die ein * intelligenter * Benutzer oder Hacker benutzen könnte: 'T <- FALSE; F <- TRUE', 'rm (liste = ls())', 'system (" rm -rf your_directories ")', 'source (" http: //.../virus.R ")'. – flodel

+14

Es gibt eine sichere Version von 'eval' (' eval.secure') im RAppArmor-Paket, die in einer Sandbox ausgeführt wird und Superuser-Rechte nur dann ausübt, wenn der übergeordnete Prozess sie besitzt. –

+1

@ORION, danke für die Sicherheitslücken! und +1 zu @mplourde für 'eval.secure' –

30

Die Sicherheitsprobleme treten nur dann wirklich auf, wenn Sie eval mit Strings aufrufen, die ein anderer Benutzer an Sie übergeben hat. Das ist eine große Sache, wenn Sie eine Anwendung erstellen, die R im Hintergrund ausführt, aber für die Datenanalyse, wo Sie Code schreiben, der von Ihnen selbst ausgeführt wird, sollten Sie sich keine Gedanken über die Auswirkungen von eval auf die Sicherheit machen.

Einige andere Probleme mit eval(parse( obwohl.

Erstens, Code mit Eval-Parse ist in der Regel viel schwieriger zu debuggen als nicht geparste Code, was problematisch ist, weil Debugging-Software twice as difficult als Schreiben es an erster Stelle ist.

Hier ist eine Funktion mit einem Fehler darin.

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

Dumm ich, ich habe über den Doppelpunkt-Operator vergessen und meinen Vektor falsch erstellt. Wenn ich versuche, diese Funktion zu erstellen, bemerkt R das Problem und gibt einen Fehler aus, der mich auf meinen Fehler hinweist.

Hier ist die Eval-Parse-Version.

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

Diese wird Quelle, da der Fehler innerhalb einer gültigen Zeichenfolge ist. Erst später, wenn wir den Code ausführen, wird der Fehler ausgelöst. Durch die Verwendung von eval-parse haben wir die Fähigkeit zur Fehlerüberprüfung der Quellzeit verloren.

Ich denke auch, dass diese zweite Version der Funktion viel schwieriger zu lesen ist.

Das andere Problem mit Eval-Parse ist, dass es viel langsamer als direkt ausgeführter Code ist. Vergleichen

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

und

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

Normalerweise gibt es eine bessere Art und Weise zu 'Computing auf der Sprache' als mit Code-Strings arbeiten; evalparse heavy-code braucht viel Sicherheit, um nach meiner Erfahrung eine sinnvolle Ausgabe zu gewährleisten.

Die gleiche Aufgabe kann normalerweise gelöst werden, indem direkt an R-Code als Sprachobjekt gearbeitet wird; Hadley Wickham hat eine nützliche Anleitung, Meta-Programmierung in R here:

Die defmacro() Funktion in der gtools Bibliothek ist mein Liebling Ersatz (kein halbherzig R pun intended) für die evalparse konstruiert

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> 

Der Vorteil dieser Methode ist, dass Sie garantiert syntaktisch legalen R-Code erhalten. Mehr zu dieser nützlichen Funktion finden Sie here:

Hoffe, dass hilft!

+1

Erstaunlich! Danke, dass du auf gtools & defmacro hingewiesen hast –