2013-04-14 5 views
7
(def evil-code (str "(" (slurp "/mnt/src/git/clj/clojure/src/clj/clojure/core.clj") ")")) 
(def r (read-string evil-code)) 

Works, aber unsichereWie kann man nicht vertrauenswürdigen Clojure-Code (nicht nur einige serialisierte Daten) sicher lesen?

(def r (clojure.edn/read-string evil-code)) 
RuntimeException Map literal must contain an even number of forms clojure.lang.Util.runtimeException (Util.java:219) 

Geht nicht ...

Wie Clojure Code (presering alle '#' s als selbst wünschenswert ist) in einen Baum sicher zu lesen? Stellen Sie sich ein Clojure-Antivirenprogramm vor, das den Code auf Bedrohungen überprüfen und mit der Datenstruktur arbeiten möchte, nicht mit reinem Text.

+1

könnte ein viel des Guten, aber ein nehmen schau dir [clojail] an (https://github.com/flatland/clojail) –

+2

Benutze read-string mit \ * read-eval \ * auf false setzen – Ankur

+0

Doc sagt das sogar mit [\ * read-eval * false] immer noch nicht entworfen, um sicher zu sein. Und wie analysiert man den Code, der auf # = angewiesen ist und Makros liest? Ich erwarte, dass sie irgendwie in Datenstrukturen erscheinen, ohne tatsächlich ausgeführt zu werden. –

Antwort

4

Zunächst sollten Sie nie clojure code direkt von nicht vertrauenswürdigen Datenquellen lesen. Sie sollten stattdessen EDN oder ein anderes Serialisierungsformat verwenden.

Seit Clojure 1.5 gibt es eine Art sicherer Weg, Strings zu lesen, ohne sie auszuwerten. Sie sollten die Variable read-eval var vor der Verwendung von read-string an false binden. In Clojure 1.4 und früher führte dies möglicherweise zu Seiteneffekten, die durch den Aufruf von Java-Konstruktoren verursacht wurden. Diese Probleme wurden seither behoben. Hier

ist einige Beispiel-Code:

(defn read-string-safely [s] 
    (binding [*read-eval* false] 
    (read-string s))) 

(read-string-safely "#=(eval (def x 3))") 
=> RuntimeException EvalReader not allowed when *read-eval* is false. clojure.lang.Util.runtimeException (Util.java:219) 

(read-string-safely "(def x 3)") 
=> (def x 3) 

(read-string-safely "#java.io.FileWriter[\"precious-file.txt\"]") 
=> RuntimeException Record construction syntax can only be used when *read-eval* == true clojure.lang.Util.runtimeException (Util.java:219) 

Bezüglich Leser Makros

Der Versand Makro (#) und getaggt Literale werden bei Lesezeit aufgerufen. In Clojure-Daten gibt es keine Repräsentation für sie, da zu dieser Zeit alle diese Konstrukte verarbeitet wurden. Soweit ich weiß, gibt es keinen eingebauten Weg, um einen Syntaxbaum des Clojure-Codes zu erzeugen.

Sie müssen einen externen Parser verwenden, um diese Informationen beizubehalten. Entweder rollen Sie Ihren eigenen benutzerdefinierten Parser oder Sie können einen Parser-Generator wie Instaparse und ANTLR verwenden. Eine vollständige Clojure-Grammatik für eine dieser Bibliotheken könnte schwer zu finden sein, aber Sie könnten eine der EDN-Grammatiken um die zusätzlichen Clojure-Formulare erweitern. Eine schnelle Google ergab an ANTLR grammar for Clojure syntax, Sie könnten es ändern, um die Konstrukte zu unterstützen, die bei Bedarf fehlen.

Es gibt auch Sjacket eine Bibliothek für Clojure-Tools, die Informationen über den Quellcode selbst behalten müssen. Es scheint wie eine gute Lösung für das, was Sie versuchen, aber ich habe keine Erfahrung damit persönlich. Gemessen an den Tests hat es Unterstützung für Lesemakros in seinem Parser.

+0

Wie man Clojure-Code programmatisch ändert und '# =' s als sich selbst zurücklässt (nicht ausführen, aber nicht ignorieren). Die Clojure-Daten werden nicht deserialisiert, sondern das Clojure-Programm gelesen und als Baum dargestellt, mit dem gearbeitet werden kann. Das Lesen mit 'edn/read-string' ist wie das Lesen von JavaScript-Code mit JSON-Parser ... –

+0

@Vi. Ich habe meine Antwort aktualisiert. –

2

Nach der current documentation Sie soll nie Verwendung read noch read-string aus nicht vertrauenswürdigen Datenquellen zu lesen.

WARNING: You SHOULD NOT use clojure.core/read or 
clojure.core/read-string to read data from untrusted sources. They 
were designed only for reading Clojure code and data from trusted 
sources (e.g. files that you know you wrote yourself, and no one 
else has permission to modify them). 

sollten Sie read-edn oder clojure.edn/read benutzen, die mit diesem Zweck konzipiert wurden.

Es gab eine long discussion in der Mailing-Liste in Bezug auf die Verwendung von Lese- und read-eval und Best Practices in Bezug auf jene.

+0

'clojure.edn/read' ist bereits in der Frage erwähnt. Es liest Datenstrukturen, die wie Clojure-Code serialisiert sind, aber es liest keinen willkürlichen Clojure-Code. Wie kann man AST aus einem beliebigen Clojure-Code sicher erstellen (vorzugsweise um es wieder in Text zu erzeugen)? –

0

Ich wollte eine alte Bibliothek, darauf hinzuweisen (verwendet in Leuchttisch), die read-string mit Techniken verwendet eine Client/Server-Kommunikation

Fetch : A ClojureScript library for Client/Server interaction vorzuschlagen.

Sie können die safe-read Methode insbesondere sehen:

(defn safe-read [s] 
    (binding [*read-eval* false] 
    (read-string s))) 

Sie die Verwendung finden können *read-eval*-false binden. Ich denke, der Rest des Codes ist es wert, für die Art von Abstraktionen, die er vorschlägt, zuzuschauen.

In einem PR wird vorgeschlagen, dass es ein Sicherheitsproblem ist, dass statt durch die Verwendung edn befestigt werden kann (... aaand auf Ihre Frage zurück):

(require '[clojure.edn :as edn]) 

(defn safe-read [s] 
    (edn/read-string s)) 
Verwandte Themen