7

Um eine Ausgabe zu generieren, verwendet eine Funktion normalerweise nur Werte ihrer Argumente. Es gibt jedoch auch Fälle, in denen die Funktion zum Generieren ihrer Ausgabe etwas aus einem Dateisystem oder aus einer Datenbank oder aus dem Internet liest. Ich hätte gerne einen einfachen und zuverlässigen Weg, um sicherzustellen, dass so etwas nicht passiert.Wie kann sichergestellt werden, dass eine Python-Funktion ihre Ausgabe nur anhand ihrer Eingabe generiert?

Eine Möglichkeit, die ich sehe, ist die Erstellung einer Whitelist von Python-Bibliotheken, die zum Lesen von Dateisystem, Datenbank oder Web verwendet werden können. Aber wenn es der richtige Weg ist, wo bekomme ich diese (potentiell riesige) Liste. Darüber hinaus möchte ich nicht die gesamte Bibliothek deaktivieren, nur weil sie zum Lesen aus dem Dateisystem verwendet werden kann. Ich möchte zum Beispiel, dass Benutzer die Pandas-Bibliothek benutzen können (um Tabellendaten zu speichern und zu manipulieren). Ich möchte nur nicht, dass sie diese Bibliothek benutzen können, um Daten aus dem Dateisystem zu lesen.

Gibt es eine Lösung für dieses Problem?

+1

Sichern Sie einen Schritt. * Warum * willst du verhindern, dass jemand von einer externen Quelle liest? – chepner

+0

Es gibt viele Gründe. Zuallererst möchte ich sicher sein, dass die Funktion in Zukunft dieselbe Ausgabe wie heute generiert. Zweitens halte ich es im Allgemeinen für eine "hässliche" Lösung, wenn eine Funktion irgendwo etwas liest. Es sollte nur sehen, was es explizit als Eingabe erhält. Wenn etwas aus einer Datei oder Datenbank gelesen werden soll, sollte es außerhalb der Funktion gelesen und als eine seiner Eingaben an die Funktion übergeben werden. – Roman

+0

Sie möchten also Code verwenden, dem Sie nicht vertrauen? –

Antwort

8

Die Antwort darauf ist nein. Was Sie suchen, ist eine Funktion, die für functional purity testet. Aber, wie in diesem Code gezeigt, gibt es keine Möglichkeit zu garantieren, dass keine Nebenwirkungen tatsächlich aufgerufen werden.

class Foo(object): 
    def __init__(self, x): 
     self.x = x 
    def __add__(self, y): 
     print("HAHAHA evil side effects here...") 
     # proceed to read a file and do stuff 
     return self 

# this looks pure... 
def f(x): return x + 1 

# but really... 
>>> f(Foo(1)) 
HAHAHA evil side effects here... 

Aufgrund der umfassenden Art und Weise Objekte können ihr Verhalten neu definieren (Feldzugriff, nennen, Betreiber Überlastung etc.), können Sie immer eine Eingabe übergeben, die eine reine Funktion unrein macht. Daher sind die einzigen reinen Funktionen diejenigen, die mit ihren Argumenten buchstäblich nichts tun ... eine Klasse von Funktionen, die im Allgemeinen weniger nützlich ist.

Natürlich, wenn Sie andere Einschränkungen angeben können, wird dies einfacher.

+0

In Ihrem Beispiel passieren "böse Effekte", weil der Benutzer der Funktion etwas "schlechtes" getan hat (der Benutzer hat eine "gute" Funktion mit einem "schlechten" Argument aufgerufen). In meinem Fall bin ich der Benutzer der Funktion. Also werde ich die Funktionen nicht "schlecht" nennen. Ich muss nur sicher sein, dass die Funktionen, die ich benutze, nicht "schlecht" sind. – Roman

+0

Das ist wichtig zu wissen im Voraus ... – PythonNut

+2

@Roman: Ihre Anforderungen waren ursprünglich viel stärker. Sie haben geschrieben: "Ich möchte, dass Benutzer in der Lage sind ... Ich möchte nur nicht, dass sie diese Bibliothek verwenden können, um Daten aus dem Dateisystem zu lesen." und jetzt schreibst du: "Ich werde die Funktionen nicht schlecht nennen." Das scheint ungewöhnlich. Sie glauben dem Benutzer, glauben aber nicht an die installierte Software? – hynekcer

4

Ihre erforderlichen Einschränkungen können auch dann aufgehoben werden, wenn Sie alle Module und alle Funktionen entfernen. Der Code kann Zugriff auf Dateien erhalten, wenn er Attribute eines beliebigen einfachen Objekts, z. der Zahl Null.

(0).__class__.__base__.__subclasses__()[40]('/etc/pas'+'swd') 

Der Index 40 ist individuell und sehr typisch für Python 2.7, aber der Index der Unterklasse <type 'file'> leicht zu finden:

[x for x in (1).__class__.__base__.__subclasses__()if'fi'+'le'in'%s'%x][0](
'/etc/pas'+'swd') 

Jede Kombination von weißer Liste und Schwarze Liste ist entweder unsicher und/oder zu restriktiv. Die pypy sandbox ist durch das Prinzip ohne Kompromisse robust:

... Diese subprocess kann beliebig nicht vertrauenswürdigen Python-Code ausführen, aber all seine Eingabe/Ausgabe ist auf ein stdin/stdout Rohr statt serialisierten des Seins direkt durchgeführt. Der äußere Prozess liest das Rohr und entscheidet, welche Befehle erlaubt sind oder nicht (Sandbox) oder sogar neu interpretiert sie anders ...

Auch eine Lösung auf Basis von seccomp Kernel-Funktion sicher genug sein kann. (blog)


Ich möchte sicher sein, dass die Funktion in Zukunft die gleiche Ausgabe als heute erzeugen.

Es ist einfach, eine Funktion zu schreiben, die schwer reproduzierbare Ergebnisse hat und es nicht leicht verhindert werden kann:

class A(object): 
    "This can be any very simple class" 
    def __init__(self, x): 
     self.x = x 
    def __repr__(self): 
     return repr(self.x) 

def strange_function(): 
    # You get a different result probably everytimes. 
    return list(set(A(i) for i in range(20))) 

>>> strange_function() 
[1, 18, 12, 5, 16, 15, 8, 2, 14, 0, 6, 19, 13, 11, 10, 9, 17, 3, 7, 4] 
>>> strange_function() 
[0, 9, 14, 3, 17, 5, 6, 11, 8, 1, 15, 7, 12, 13, 2, 10, 16, 4, 19, 18] 

... auch wenn Sie everythng entfernen, die auf Zeit, Zufallszahl abhängt Generator, Reihenfolge basierend auf Hash-Funktion usw., ist es auch einfach, eine Funktion zu schreiben, die manchmal die verfügbare Speicher- oder Zeitlimitgrenze überschreitet und manchmal ein Ergebnis liefert.


EDIT:
Roman, schrieb sie vor kurzem, dass Sie sicher, dass Sie den Benutzer glauben kann. Dann existiert eine realistische Lösung. Es ist die Eingabe und Ausgabe von einer Funktion zu verifizieren, indem Sie es in eine Datei aufzeichnen und auf einer virtuellen Maschine mit einem Remote IPython notebook überprüfen (schönes, kurzes Tutorial Video, Unterstützung für Remote-Computing out of box, Neustart des Backend-Dienstes per Web) Dokument-Menü aus dem Browser in einer Sekunde, ohne Verlust von Daten (Eingabe/Ausgabe) im Notebook (HTML-Dokument), weil es dynamisch erstellt wird Schritt für Schritt durch unsere Aktivität das Auslösen des Javascript, das das Remote-Backend aufruft).

Sie müssen nicht an internen Aufrufen interessiert sein, nur an der globalen Ein- und Ausgabe, bis Sie einen Unterschied finden. Die virtuelle Maschine sollte die Ergebnisse unabhängig und reproduzierbar verifizieren können. Konfigurieren Sie die Firewall, für die der Computer Verbindungen von Ihnen akzeptiert, aber keine ausgehende Verbindung initiieren kann. Konfigurieren Sie das Dateisystem so, dass keine Daten vom aktuellen Benutzer gespeichert werden können und daher nicht vorhanden sind, mit Ausnahme von Softwarekomponenten. Deaktivieren Sie die Datenbankdienste. Überprüfen Sie die Eingabe/Ausgabe der Ergebnisse in zufälliger Reihenfolge, oder starten Sie zwei IPython-Notebook-Dienste an verschiedenen Ports und wählen Sie für jede Befehlszeile auf dem Notebook ein zufälliges Backend aus oder starten Sie den Back-End-Prozess häufig bevor etwas Wichtiges. Wenn Sie einen Unterschied finden, debuggen Sie Ihren Code und beheben Sie ihn.

Sie können es ohne "Notebook" schließlich nur mit IPython Remote Computing automatisieren, nachdem Sie keine Interaktivität benötigen.

+1

Es sollte beachtet werden, dass Sie auch Zufallszahlen aus der zufälligen Speicheradresse eines Objekts erhalten können. 'Klasse A: passiere; str (A)' – PythonNut

Verwandte Themen