tätsächlich eine sicherheitslücke?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Gast

hi zusammen,...

bezugnehmend auf diesen thread
http://python.sandtner.org/viewtopic.php?t=2847&start=2

will ich nochmal diskutieren ob die anwendung von

Code: Alles auswählen

eval()
wie ich sie gemacht habe wirklich eine sicherheitslücke ist.

abgesehen von der diskussion, war ich wirklich daran interessiert, allein schon weil mich das auch in python weitergebracht hätte, das programm so abzuändern, dass nur dann

Code: Alles auswählen

eval()
ausgeführt wird wenn keine anweisung übergeben wurde.

ich habe aber mal nachgedacht, wann denn überhaupt so ein negativer fall eintreten könnte. es müsste jmd. von aussen zugriff auf den pc haben, und wissen
1. dass ich dieses programm geschrieben habe,
2. dass in diesem programm eine funktion vorkommt, die nicht nur erlaubt, dass datentypen per gui eingegeben und dann in einer datei abgespeichert werden können
3. wie dieses programm heisst
usw. usw.

wenn er in das system eindringt und schaden anrichten will, wird er sich einer konsole bedienen (warum kompliziert, wenn es einfach auch geht) und dort, je nachdem ob er mit benutzerrechten oder root rechten agieren kann im entsprechenden verzeichnis z.b.

Code: Alles auswählen

rm -rf 
ausführen.

verwendet er mein programm können die anweisungen auch nur dort ausgeführt werden wo ich mit benutzerrechten entsprechende aktionen ausführen kann.

es gibt anweisungen, die gefährlich sind, klar, sind dann aber über eine pythonkonsole schneller auszuführen, zu der er ja dann auch zugang hat.

es ist ein programm, das mit verantwortung zu verwenden ist.

das ist aber öfters so

Code: Alles auswählen

 rm -rf
kann ich ja immer verwenden, werde es aber hoffentlich nur tun, wenn es sinn macht, oder ist diese anweisung eigentlich eine sicherheitslücke???

vielleicht habe ich ja was übersehen, aber ich denke, ich kann ohne weiters

Code: Alles auswählen

eval()
für den im thread aufgezeigten thread verwenden.

eine sicherheitslücke ist für mich, wenn eine anwendung, z.b. ein browser einen fehler aufweist, der den zugang zum system ermöglicht. in meinem fall muss er ja schon zugang haben!

mfg

rolgal

Edit (Leonidas): Code in Python Tags gesetzt.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Naja, im Pronzip hast du da schon recht, aber anderer Seits läßt sich fast immer eine alternative zu eval() programmieren, also warum eine Sicherheitslücke mehr, auch wenn sie noch so klein ist?
Gast

hi jens,

wir (leute aus dem forum), ich, haben noch keine wirkliche alternative gefunden, die eval() ersetzen könnte in diesem zusammenhang.


mfg

rolgal
BlackJack

Das Rezept "Safe" Eval funktioniert nicht?

Das Problem mit eval() ist auch, dass Code oft länger "lebt" als man das vermutet. Dann wird irgendwann mal funktionierender Code in ein anderes Projekt kopiert, oder das ursprüngliche Programm wird um die Möglichkeit erweitert die Eingabe auch aus einer Datei zu lesen. Und schon gibt es mehr Wege für Daten in diese "böse" Anweisung.

Grundsätzlich würde ich zu `eval()` sagen, dass man es nur einsetzen sollte wenn ganz bestimmt keine Daten da hineingehen, die von Benutzern in irgendeiner Form eingegeben werden.
Gast

hi blackjack,....
Das Problem mit eval() ist auch, dass Code oft länger "lebt" als man das vermutet. Dann wird irgendwann mal funktionierender Code in ein anderes Projekt kopiert, oder das ursprüngliche Programm wird um die Möglichkeit erweitert die Eingabe auch aus einer Datei zu lesen. Und schon gibt es mehr Wege für Daten in diese "böse" Anweisung.
das muss ich mir jetzt wieder länger durch den kopf gehen lassen, vielleicht auch erfahrungen sammeln. auf jeden fall interessant.
Grundsätzlich würde ich zu `eval()` sagen, dass man es nur einsetzen sollte wenn ganz bestimmt keine Daten da hineingehen, die von Benutzern in irgendeiner Form eingegeben werden.
das habe ich eh schon erklärt, dass ich der einzige benutzer bin und dann ist es nicht gefährlicher, als wenn ich mich als root an der konsole anmelde und mit mächtigen befehlen hantiere.


Das Rezept "Safe" Eval funktioniert nicht?
wird es wohl, aber da blick ich ehrlich gesagt noch nicht durch bei dem code, ist mir noch zu hoch, merkt man ja sofort vom stil her, dass das ein ganz anderes niveau ist wie ich programmiere, ich will das schon weitestgehend verstehen was ich verwende, zumindest weitestgehend :D

grüße

rolgal
BlackJack

Eigentlich ist das relativ simpel. Der Hauptteil wird vom compiler-Modul erledigt. Das übersetzt den gegebenen Quelltext in einen Abstrakten Syntax-Baum (AST). Die Klassen sind dann nur noch dazu da, um diesen Baum abzuarbeiten und daraus die Konstanten zu holen. Falls ein "Name" gefunden wird, dann steht da eine Zeichenkette die nicht in Anführungszeichen steht, also verboten ist weil potentiell gefährlich. Dann gibt's eine Ausnahme.
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hi,
Ich habe mich mal daran versucht eine eigene Variante von SaveEval zu schreiben.
Der Code sieht folgendermaßen aus:

Code: Alles auswählen

# -*- coding: iso-8859-1 -*-

import re
import compiler

#Default allow object typs see Python Dokumentation => '19.3.1 AST Nodes'
_OBJECTS = ['Dict', 'Name', 'List', 'Tuple', 'Const', 'UnarySub']
_FUNCIONS = ['set', 'wx.Point', 'wx.Size']

class UnsafeSourceError(Exception):
    """Unsave error"""
    def __init__(self, sourcecode, error_obj):
        self.sourcecode = sourcecode
        self.error_obj = error_obj

    def __repr__(self):
        """Print error"""
        return ("Following source code string is not declares as savely "
            "for eval!\nSource code: %s\nObject(s) '%s' not allowed!" %
            (self.sourcecode, ", ".join(self.error_obj)))
    __str__ = __repr__

class Checker:
    def __init__(self):
        """Initialize"""
        self.obj = _OBJECTS
        self.call_func = _FUNCIONS
        self.call_func and self.obj.append('CallFunc')
        self.eval_is_allow = True
        self.error_obj = []

    def Parse(self, node):
        """
        node = The ast node
        depth = recursion depth
        """
        for child in node.getChildNodes():
            obj_name = child.__class__.__name__
            if obj_name not in self.obj and obj_name != 'Getattr':
                self.eval_is_allow = False
                self.error_obj.append(obj_name)
            elif obj_name == 'CallFunc':
                names = re.findall("'(.+?)'", str(child.node))
                name = ".".join(names)
                if name not in self.call_func:
                    self.eval_is_allow = False
                    self.error_obj.append(name)
            self.Parse(child)
        return self.eval_is_allow

def Eval(s):
    """
    Evaluate the source code string
    s = source code string
    """
    ast_tree = compiler.parse(s, "eval")
    checker = Checker()
    parse_ok = checker.Parse(ast_tree)
    if parse_ok:
        #Load moduls
        for ms in _FUNCIONS:
            ms = ms.split(".")
            for m in range(1,len(ms)):
                exec "import %s" % ".".join(ms[:m])
        #Return allowed evaluating string
        return eval(s)
    else:
        raise UnsafeSourceError(s, checker.error_obj)

if __name__ == "__main__":
    pass
Über die zwei Listen - _OBJECTS und _FUNCIONS - kann man genau die zugelassenen Objekte und Funktionen definieren.

Was haltet ihr von dieser Lösung? Gibt es Verbesserungsmöglichkeiten?
Gruß, Harry
Antworten