Esistono problemi di sicurezza nell'utilizzo di stringhe F Python con input dell'utente
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
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
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.