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

Works, ale niebezpieczneJak bezpiecznie odczytać niezaufany kod Clojure (nie tylko niektóre zserializowanych danych)?

(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) 

Nie działa ...

Jak odczytać kod Clojure (presering wszystkie '#' s jak się jest pożądane) w drzewo bezpiecznie? Wyobraź sobie program antywirusowy Clojure, który chce skanować kod pod kątem zagrożeń i chce pracować ze strukturą danych, a nie z tekstem prostym.

+1

Może być przesada, ale wziąć spójrz na [clojail] (https://github.com/flatland/clojail) –

+2

Użyj łańcucha do odczytu z \ * read-eval \ * ustaw na false – Ankur

+0

Doc mówi, że nawet z [\ * read-eval * false] it wciąż nie zaprojektowane, aby być bezpieczne. A jak analizować kod, który polega na # = i czytać makra? Oczekuję, że pojawią się one w strukturach danych bez faktycznego wykonania. –

Odpowiedz

4

Po pierwsze nie powinieneś nigdy czytać kodu clojure bezpośrednio z niezaufanych źródeł danych. Zamiast tego należy użyć EDN lub innego formatu serializacji.

Mówiąc to od czasu Clojure 1.5 istnieje pewien rodzaj bezpiecznego sposobu odczytywania ciągów bez ich oceniania. Powinieneś związać zmienną read-eval var z wartością false przed użyciem ciągu znaków do przeczytania. W Clojure 1.4 i wcześniejszych może to powodować skutki uboczne wywołane przez konstruktory Java. Te problemy zostały już naprawione.

Oto przykładowy kod:

(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) 

chodzi czytelnik makra

Wysyłka makro (#) i oznaczone literały są wywoływane w czasie odczytu. Nie ma reprezentacji dla nich w danych Clojure, ponieważ do tego czasu wszystkie te konstrukty zostały przetworzone. O ile mi wiadomo, nie ma kompilacji w celu wygenerowania drzewa składni kodu Clojure.

Będziesz musiał użyć zewnętrznego parsera, aby zachować te informacje. Możesz uruchomić własny niestandardowy analizator składni lub użyć generatora analizatora składni, takiego jak Instaparse i ANTLR. Kompletna gramatyka Clojure dla każdej z tych bibliotek może być trudna do znalezienia, ale można rozszerzyć jedną z gramatyk EDN, aby uwzględnić dodatkowe formularze Clojure. Szybki google ujawnił an ANTLR grammar for Clojure syntax, możesz go zmienić, aby obsługiwał konstrukcje, których w razie potrzeby brakuje.

Istnieje również biblioteka stworzona dla narzędzi Clojure, które muszą zachować informacje o samym kodzie źródłowym. Wygląda na to, że dobrze pasuje do tego, co próbujesz zrobić, ale nie mam z nim żadnego osobistego doświadczenia. Sądząc z testów, posiada wsparcie dla makr czytnika w jego parserze.

+0

Jak programowo zmienić kod Clojure, pozostawiając '# =' s same w sobie (nie należy wykonywać, ale nie ignoruj). Nie przekształcają danych Clojure, ale czytają program Clojure i prezentują go jako drzewo, z którym można pracować. Czytanie z 'edn/read-string' jest jak próba odczytu kodu JavaScript przy pomocy parsera JSON ... –

+0

@Vi. Zaktualizowałem swoją odpowiedź. –

2

Według current documentation powinieneś nigdy użytku read ani read-string czytać z niezaufanych źródeł danych.

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). 

Należy użyć read-edn lub clojure.edn/read, które zostały zaprojektowane z myślą o tym celu.

Na liście dyskusyjnej znajdował się long discussion dotyczący korzystania z przeczytanych i read-eval oraz najlepszych praktyk dotyczących tych.

+0

'clojure.edn/read' jest już wymienione w pytaniu. Odczytuje struktury danych serializowane jak kod Clojure, ale nie odczytuje arbitralnego kodu clojure. Jak bezpiecznie budować AST z dowolnego kodu clojure (najlepiej, aby móc go wygenerować z powrotem do tekstu)? –

0

Chciałem podkreślić starą bibliotekę (używany w LightTable), który używa read-string z technik zaproponować komunikacji klient/serwer

Fetch : A ClojureScript library for Client/Server interaction.

można zobaczyć w szczególności metodę safe-read:

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

można zobaczyć zastosowanie wiążących *read-eval* do false. Myślę, że reszta kodu jest warta oglądania dla tego rodzaju abstrakcji, jakie proponuje.

W PR, sugeruje się, że istnieje problem bezpieczeństwa, który można przymocować za pomocą edn zamiast (... aaand powrotem na swoje pytanie):

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

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