Esistono problemi di sicurezza nell'utilizzo di stringhe F Python con input dell'utente

Sep 16 2020

sfondo

Qualche tempo fa ho iniziato a usare le stringhe F in Python, ma ricordavo di aver visto alcuni problemi di sicurezza nell'usarle con l'input dell'utente, quindi ho deciso di non usarle per quelle situazioni.

Domanda

Ci sono problemi di sicurezza nell'usare stringhe python f con l'input dell'utente. Ad esempio, è possibile che un utente abbia accesso a informazioni a cui non dovrebbe avere accesso.

Esempio

$ ./hello.py mike
hi mike
#!/usr/bin/python3

import sys

secrete = 'my secrete'

print(F"hi {sys.argv[1]}")

Questo programma è fondamentalmente un ciao mondo di base che accetta l'input dell'utente. È possibile che un utente malintenzionato fornisca un input che esfiltrerebbe la variabile secreta o altri dati preziosi?

Risposte

2 Arminius Oct 17 2020 at 18:22

Le stringhe f di Python sono effettivamente più sicure. Usali!


La formattazione della stringa può essere pericolosa quando una stringa di formato dipende da dati non attendibili. Quindi, quando si utilizza str.format()o %-formatting, è importante utilizzare stringhe di formato statico o disinfettare le parti non attendibili prima di applicare la funzione di formattazione. Al contrario, le stringhe f non sono in realtà semplici stringhe, ma più simili a zucchero sintattico per concatenare stringhe ed espressioni. In quanto tale, il formato di una stringa f è predeterminato e non consente parti dinamiche (potenzialmente non attendibili) in primo luogo.

Formattazione vecchio stile con str.format()

>>> data_str = 'bob'
>>> format_str = 'hello {name}!'
>>> format_str.format(name=data_str)
'hello bob!'

Qui, il tuo interprete Python non conosce la differenza tra una stringa di dati e una stringa di formato. Chiama solo una funzione, str.format()che esegue un algoritmo di sostituzione sul valore della stringa di formato al momento dell'esecuzione. Quindi, prevedibilmente, il formato è solo una semplice stringa con parentesi graffe al suo interno:

>>> import dis
>>> dis.dis("'hello {name}!'")
  1           0 LOAD_CONST               0 ('hello {name}!')
              2 RETURN_VALUE

Formattazione di nuovo stile con stringhe f

>>> data_str = 'bob'
>>> f'hello {data_str}!'
'hello bob!'

Qui, f'hello {data_str}!'può apparire come una costante stringa, ma non lo è. L'interprete non analizza la parte tra {...}come parte della stringa che può essere espansa in seguito, ma come un'espressione separata:

>>> dis.dis("f'hello {name}!'")
  1           0 LOAD_CONST               0 ('hello ')
              2 LOAD_NAME                0 (name)
              4 FORMAT_VALUE             0
              6 LOAD_CONST               1 ('!')
              8 BUILD_STRING             3
             10 RETURN_VALUE

Quindi, pensa a "hi {sys.argv[1]}"come (approssimativamente) zucchero sintattico per "hi " + sys.argv[1]. In fase di esecuzione, l'interprete non sa o non si preoccupa nemmeno che tu abbia usato una stringa f. Vede solo le istruzioni per costruire una stringa da una costante "hi "e il valore formattato di sys.argv[1].

Esempio vulnerabile

Ecco un'app Web di esempio che utilizza str.format()in modo vulnerabile:

from http.server import HTTPServer, BaseHTTPRequestHandler

secret = 'abc123'

class Handler(BaseHTTPRequestHandler):
    name = 'funtimes'
    msg = 'welcome to {site.name}'
    def do_GET(self):
        res = ('<title>' + self.path + '</title>\n' + self.msg).format(site=self)
        self.send_response(200)
        self.send_header('content-type', 'text/html')
        self.end_headers()
        self.wfile.write(res.encode())

HTTPServer(('localhost', 8888), Handler).serve_forever()
$ python3 example.py $ curl 'http://localhost:8888/test'
<title>/test</title>
welcome to funtimes

attacco

Quando la resstringa viene creata, viene utilizzata self.pathcome parte della stringa di formato. Poiché self.pathè controllato dall'utente, possiamo usarlo per modificare la stringa di formato ed esfiltrare la variabile globale secret:

$ curl -g 'http://localhost:8888/XXX{site.do_GET.__globals__[secret]}'
<title>/XXXabc123</title>
welcome to funtimes
1 multithr3at3d Sep 17 2020 at 07:23

Se questa caratteristica del linguaggio di base fosse così difettosa, probabilmente non sarebbe affatto una caratteristica. Fintanto che il contenuto della stringa di formato è controllato dal programmatore in fase di sviluppo, non c'è nulla che un utente possa fare per abusarne.

Il contenuto delle parentesi graffe viene valutato, ma il risultato di tale valutazione non viene valutato di nuovo (cioè sys.argv[1]viene valutato a "1 + 1", ma non viene valutato di nuovo, come avete visto).

Il problema sorge quando un utente è in grado di inserire dati in una stringa prima che venga formattata; guarda questo esempio di sfida . Anche se questo non è per una stringa f, è una buona dimostrazione degli attacchi che sono possibili se l'utente è autorizzato a controllare la formattazione.