2011-12-31 4 views
7

Sto scrivendo un programma in stile wiki personale in python che memorizza i file di testo in una directory configurabile dall'utente.Convalidare un nome file in python

Il programma dovrebbe essere in grado di prendere una stringa (ad esempio "pippo") da un utente e creare un nome file di foo.txt. L'utente sarà in grado di creare il file all'interno della directory wiki e le barre creeranno una sottodirectory (ad esempio "foo/bar" diventa "(path-to-wiki) /foo/bar.txt").

Qual è il modo migliore per verificare che l'ingresso sia il più sicuro possibile? Cosa devo fare attenzione (directory traversal - "../"?, Null bytes - "\ 0"?).

Mi rendo conto che l'input dell'utente per i nomi di file non è mai sicuro al 100%, ma il programma verrà eseguito solo localmente e voglio solo evitare eventuali errori/glitch comuni.

+1

Cosa target? Quali versioni di Python? –

+0

@ IgnacioVazquez-Abrams: sì, ma i file di testo in chiaro nel file system hanno altri vantaggi. – Puzzled79

+0

@ g.d.d.c: Python 2.7 e/o 3.2 e principalmente MacOS/Linux. – Puzzled79

risposta

8

È possibile applicare all'utente di creare un file/directory all'interno wiki normalizzando il percorso con os.path.normpath e quindi verificare se il percorso inizia con diciamo '(path-to-wiki)'

os.path.normpath('(path-to-wiki)/foo/bar.txt').startswith('(path-to-wiki)') 

Per assicurare che il percorso/nome file immesso dall'utente non contenga nulla di brutto, è possibile forzare l'utente a immettere un percorso o un nome file in Alfabeto inferiore/superiore, Cifre numeriche o può essere trattino o trattino basso.

Poi si può sempre controllare il nome del file normalizzato utilizzando una simile espressione regolare

userpath=os.path.normpath('(path-to-wiki)/foo/bar.txt') 
re.findall(r'[^A-Za-z0-9_\-\\]',userpath) 

In sintesi

se userpath=os.path.normpath('(path-to-wiki)/foo/bar.txt') poi

if not os.path.normpath('(path-to-wiki)/foo/bar.txt').startswith('(path-to-wiki)') 
    or re.search(r'[^A-Za-z0-9_\-\\]',userpath): 
    ... Do what ever you want with an invalid path 
0

si può solo convalidare tutti i personaggi sono alfanumerici da stampare ascii tranne i caratteri '', '.', e '/' quindi rimuovi tutte le istanze di combinazioni errate ...

safe_string = str() 
for c in user_supplied_string: 
    if c.isalnum() or c in [' ','.','/']: 
     safe_string = safe_string + c 

while safe_string.count("../"): 
    # I use a loop because only replacing once would 
    # leave a hole in that a bad guy could enter ".../" 
    # which would be replaced to "../" so the loop 
    # prevents tricks like this! 
    safe_string = safe_string.replace("../","./") 
# Get rid of leading "./" combinations... 
safe_string = safe_string.lstrip("./") 

Questo è quello che farei, non so quanto sia pitonico, ma dovrebbe lasciarti abbastanza al sicuro. Se si voleva per convalidare e non convertire poi si può solo fare un test per l'uguaglianza, dopo che in questo modo:

valid = save_string == user_supplied_string 
if not valid: 
    raise Exception("Sorry the string %s contains invalid characters" % user_supplied_string) 

Alla fine entrambi gli approcci probabilmente funzionerebbe, trovo questo metodo si sente un po 'più esplicito e dovrebbe anche elimina tutti i caratteri strani/non appropriati come '\ t', '\ r' o '\ n' Cheers!

3

Armin Ronacher ha un post sul blog su questo soggetti (e altri): http://lucumr.pocoo.org/2010/12/24/common-mistakes-as-web-developer/

Queste idee sono implementate come la funzione safe_join() in Flask:

sistema operativo
def safe_join(directory, filename): 
    """Safely join `directory` and `filename`. 

    Example usage:: 

    @app.route('/wiki/<path:filename>') 
    def wiki_page(filename): 
    filename = safe_join(app.config['WIKI_FOLDER'], filename) 
    with open(filename, 'rb') as fd: 
    content = fd.read() # Read and process the file content... 

    :param directory: the base directory. 
    :param filename: the untrusted filename relative to that directory. 
    :raises: :class:`~werkzeug.exceptions.NotFound` if the resulting path 
    would fall out of `directory`. 
    """ 
    filename = posixpath.normpath(filename) 
    for sep in _os_alt_seps: 
     if sep in filename: 
      raise NotFound() 
    if os.path.isabs(filename) or filename.startswith('../'): 
     raise NotFound() 
    return os.path.join(directory, filename)