Assicurarsi che il codice non sicuro non venga utilizzato accidentalmente


10

Una funzione f()utilizza eval()(o qualcosa di così pericoloso) con i dati che ho creato e archiviato local_filesulla macchina che esegue il mio programma:

import local_file

def f(str_to_eval):
    # code....
    # .... 
    eval(str_to_eval)    
    # ....
    # ....    
    return None


a = f(local_file.some_str)

f() è sicuro da eseguire poiché le stringhe che fornisco sono mie.

Tuttavia, se mai decidessi di usarlo per qualcosa di non sicuro (ad esempio l'input dell'utente) le cose potrebbero andare terribilmente storto . Inoltre, se local_filesmette di essere locale, ciò creerebbe una vulnerabilità poiché avrei bisogno di fidarmi della macchina che fornisce anche quel file.

Come devo assicurarmi di non "dimenticare" mai che questa funzione non è sicura da usare (a meno che non siano soddisfatti criteri specifici)?

Nota: eval()è pericoloso e di solito può essere sostituito da qualcosa di sicuro.


1
Di solito mi piace seguire le domande poste piuttosto che dire "non dovresti farlo", ma in questo caso farò un'eccezione. Sono abbastanza certo che non c'è motivo per cui dovresti usare eval. Queste funzioni sono incluse in linguaggi come PHP e Python come una sorta di prova di concetto di quanto si può fare con i linguaggi interpretati. È fondamentalmente inconcepibile che tu possa scrivere codice che crea codice, ma che non puoi codificare la stessa logica in una funzione. Python probabilmente ha callback e associazioni di funzioni che ti permetteranno di fare ciò di cui hai bisogno
user1122069

1
" Dovrei fidarmi anche della macchina che fornisce quel file " - devi sempre fidarti della macchina su cui stai eseguendo il tuo codice.
Bergi,

Inserisci un commento nel file dicendo "Questa stringa viene valutata; ti preghiamo di non inserire qui un codice dannoso"?
user253751

@immibis Un commento non sarebbe sufficiente. Ovviamente avrò un enorme "ATTENZIONE: questa funzione usa eval ()" nei documenti della funzione, ma preferirei fare affidamento su qualcosa di molto più sicuro di così. Ad esempio (a causa di un tempo limitato) non rileggo sempre i documenti di una funzione. Né penso che dovrei essere. Ci sono modi più veloci (cioè più efficienti) per sapere che qualcosa non è sicuro, come i metodi collegati da Murphy nella sua risposta.
Paradosso di Fermi il

@Fermiparadox quando elimini la domanda diventa senza un esempio utile. Stai parlando ora di una funzione che non è sicura a causa di una supervisione deliberata, come non sfuggire ai parametri SQL. Intendi codice di prova non sicuro per un ambiente di produzione? Ad ogni modo, ora ho letto il tuo commento sulla risposta di Amon - sostanzialmente stavi cercando come proteggere la tua funzione.
user1122069

Risposte:


26

Definire un tipo per decorare input "sicuri". Nella tua funzione f(), asserisci che l'argomento ha questo tipo o genera un errore. Sii abbastanza intelligente da non definire mai una scorciatoia def g(x): return f(SafeInput(x)).

Esempio:

class SafeInput(object):
  def __init__(self, value):
    self._value = value

  def value(self):
    return self._value

def f(safe_string_to_eval):
  assert type(safe_string_to_eval) is SafeInput, "safe_string_to_eval must have type SafeInput, but was {}".format(type(safe_string_to_eval))
  ...
  eval(safe_string_to_eval.value())
  ...

a = f(SafeInput(local_file.some_str))

Mentre questo può essere facilmente aggirato, rende più difficile farlo per caso. Le persone potrebbero criticare un tale tipo di controllo draconiano, ma perderebbero il punto. Dal momento che ilSafeInput tipo è solo un tipo di dati e non una classe orientata agli oggetti che potrebbe essere sottoclassata, il controllo dell'identità del tipo con type(x) is SafeInputè più sicuro del controllo della compatibilità del tipo con isinstance(x, SafeInput). Dal momento che vogliamo imporre un tipo specifico, ignorare il tipo esplicito e semplicemente fare la tipizzazione anatra implicita non è soddisfacente qui. Python ha un sistema di tipi, quindi usiamolo per rilevare possibili errori!


5
Potrebbe essere necessario un piccolo cambiamento. assertviene rimosso automaticamente poiché utilizzerò il codice Python ottimizzato. if ... : raise NotSafeInputErrorSarebbe necessario qualcosa del genere .
Paradosso di Fermi,

4
Se stai seguendo questa strada, ti consiglio vivamente di passare eval()a un metodo SafeInput, in modo da non aver bisogno di un controllo esplicito del tipo. Dai al metodo un nome che non è usato in nessuna delle tue altre classi. In questo modo, puoi ancora deridere il tutto a scopo di test.
Kevin

@Kevin: visto che evalha accesso alle variabili locali, è possibile che il codice dipenda da come muta le variabili. Spostando l'Eval in un altro metodo, non sarà più in grado di mutare le variabili locali.
Sjoerd Job Postmus

Un ulteriore passo potrebbe essere quello di fare in modo che il SafeInputcostruttore prenda un nome / handle del file locale ed esegua la lettura stessa, garantendo che i dati provengano effettivamente da un file locale.
Owen

1
Non ho molta esperienza con Python, ma non è meglio lanciare un'eccezione? Nella maggior parte delle lingue le asserzioni possono essere disattivate e sono (come ho capito bene) più per il debug che per la sicurezza. Le eccezioni e l'uscita dalla console garantiscono che il codice successivo non verrà eseguito.
user1122069

11

Come ha sottolineato Joel una volta, fai sembrare sbagliato il codice sbagliato (scorri verso il basso fino alla sezione "La vera soluzione", o meglio ancora leggilo completamente; ne vale la pena, anche se indirizza la variabile anziché i nomi delle funzioni). Quindi suggerisco umilmente di rinominare la tua funzione in qualcosa del genere f_unsafe_for_external_use()- molto probabilmente ti inventerai uno schema di abbreviazioni da solo.

Modifica: Penso che il suggerimento di Amon sia un'ottima variante basata su OO sullo stesso tema :-)


0

Completando il suggerimento di @amon, puoi anche pensare a combinare il modello di strategia qui, così come il modello di oggetti metodo.

class AbstractFoobarizer(object):
    def handle_cond(self, foo, bar):
        """
        Handle the event or something.
        """
        raise NotImplementedError

    def run(self):
        # do some magic
        pass

E poi un'implementazione 'normale'

class PrintingFoobarizer(AbstractFoobarizer):
    def handle_cond(self, foo, bar):
        print("Something went very well regarding {} and {}".format(foo, bar))

E un'implementazione admin-input-eval

class AdminControllableFoobarizer(AbstractFoobarizer):
    def handle_cond(self, foo, bar):
        # Look up code in database/config file/...
        code = get_code_from_settings()
        eval(code)

(Nota: con un esempio del mondo reale, potrei essere in grado di dare un esempio migliore).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.