2011-12-31 41 views
7

Estoy escribiendo un programa personal de estilo wiki en python que almacena archivos de texto en un directorio configurable por el usuario.Validar un nombre de archivo en python

El programa debería poder tomar una cadena (por ejemplo, "foo") de un usuario y crear un nombre de archivo de foo.txt. El usuario solo podrá crear el archivo dentro del directorio wiki, y las barras crearán un subdirector (por ejemplo, "foo/bar" se convierte en "(ruta a wiki) /foo/bar.txt").

¿Cuál es la mejor manera de verificar que la entrada sea lo más segura posible? ¿Qué debo tener en cuenta (recorrido de directorios - "../"?, Bytes nulos - "\ 0"?).

Me doy cuenta de que tomar la entrada del usuario para los nombres de los archivos nunca es 100% seguro, pero el programa solo se ejecutará localmente y solo quiero protegerme de cualquier error/falla.

+1

Qué objetivo? ¿Qué versiones de python? –

+0

@ IgnacioVazquez-Abrams: sí, pero los archivos de texto plano en el sistema de archivos tienen otros beneficios. – Puzzled79

+0

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

Respuesta

8

Puede exigir al usuario crear un archivo/directorio dentro wiki normalizando el camino con os.path.normpath y luego comprobar si la ruta se inicia con digamos '(ruta-a-wiki)'

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

Para asegurar que la ruta de acceso/nombre de archivo del usuario no contenga nada desagradable, puede forzar al usuario a ingresar una ruta o un nombre de archivo de Alfa Inferior/Superior, Dígitos Numéricos o puede ser guión o guión bajo.

entonces siempre se puede comprobar el nombre de fichero normalizado utilizando una expresión regular similares

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

En resumen

si userpath=os.path.normpath('(path-to-wiki)/foo/bar.txt') continuación

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

Sólo pudo validar todos los caracteres alfanuméricos imprimible ascii excepto por los caracteres '', '.' y '/', simplemente elimine todas las instancias de combinaciones incorrectas ...

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("./") 

Eso es lo que haría, no sé cuán pitónico es pero debería dejarte bastante seguro. Si quería validar y no convertir a continuación, sólo podría hacer una prueba para la igualdad después de ese modo:

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

Al final ambos enfoques probablemente funcionarían, encuentro este método se siente un poco más explícita y también debe elimine los caracteres extraños o no apropiados, como '\ t', '\ r' o '\ n' ¡Salud!

3

Armin Ronacher tiene una entrada de blog sobre este y otros temas(): http://lucumr.pocoo.org/2010/12/24/common-mistakes-as-web-developer/

Estas ideas se implementan como la función safe_join() en frasco:

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) 
Cuestiones relacionadas