Pregunta Cree un archivo de forma segura si y solo si no existe con python


Deseo escribir en un archivo en función de si ese archivo ya existe o no, solo escribiendo si aún no existe (en la práctica, deseo seguir probando archivos hasta que encuentre uno que no exista).

El siguiente código muestra una forma en la que un atacante potencial podría insertar un enlace simbólico, como se sugiere en esta publicación entre una prueba para el archivo y el archivo que se está escribiendo. Si el código se ejecuta con permisos lo suficientemente altos, esto podría sobrescribir un archivo arbitrario.

¿Hay alguna manera de resolver este problema?

import os
import errno

file_to_be_attacked = 'important_file'

with open(file_to_be_attacked, 'w') as f:
    f.write('Some important content!\n')

test_file = 'testfile'

try:
    with open(test_file) as f: pass
except IOError, e:

    # symlink created here
    os.symlink(file_to_be_attacked, test_file)

    if e.errno != errno.ENOENT:
        raise
    else:
        with open(test_file, 'w') as f:
            f.write('Hello, kthxbye!\n')

74
2018-06-11 11:03


origen


Respuestas:


Editar: Ver también La respuesta de Dave Jones: de Python 3.3, puedes usar x bandera para open() para proporcionar esta función.

Respuesta original a continuación

Sí, pero no usando el estándar de Python open() llamada. Tendrás que usar os.open() en su lugar, lo que le permite especificar indicadores al código C subyacente.

En particular, quieres usar O_CREAT | O_EXCL. Desde la página del manual para open(2) debajo O_EXCL en mi sistema Unix:

Asegúrese de que esta llamada cree el archivo: si esta bandera se especifica junto con O_CREAT, y el nombre de ruta ya existe, luego open() fallará. El comportamiento de O_EXCL no está definido si O_CREAT no está especificado

Cuando se especifican estos dos indicadores, no se siguen los enlaces simbólicos: si el nombre de ruta es un enlace simbólico, entonces open() falla independientemente de a dónde apunta el enlace simbólico.

O_EXCL  solo se admite en NFS cuando se usa NFSv3 o posterior en kernel 2.6 o posterior. En entornos donde NFS O_EXCL no se proporciona soporte, los programas que dependen de él para realizar tareas de bloqueo contendrán una condición de carrera.

Por lo tanto, no es perfecto, pero AFAIK es lo más cerca que puedes llegar para evitar esta condición de carrera.

Editar: las otras reglas de uso os.open() en lugar de open() siguen aplicando. En particular, si desea utilizar el descriptor de archivo devuelto para leer o escribir, necesitará uno de los O_RDONLY, O_WRONLY o O_RDWR banderas también.

Todos O_* banderas están en Python os módulo, por lo que necesitarás import os y use os.O_CREAT etc.

Ejemplo:

import os
import errno

flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY

try:
    file_handle = os.open('filename', flags)
except OSError as e:
    if e.errno == errno.EEXIST:  # Failed as the file already exists.
        pass
    else:  # Something unexpected went wrong so reraise the exception.
        raise
else:  # No exception, so the file must have been created successfully.
    with os.fdopen(file_handle, 'w') as file_obj:
        # Using `os.fdopen` converts the handle to an object that acts like a
        # regular Python file object, and the `with` context manager means the
        # file will be automatically closed when we're done with it.
        file_obj.write("Look, ma, I'm writing to a new file!")

77
2018-06-11 11:53



Como referencia, Python 3.3 implementa un nuevo 'x' modo en el open() función para cubrir este caso de uso (crear solo, fallar si el archivo existe). Tenga en cuenta que 'x' modo se especifica por sí mismo. Utilizando 'wx' resultados en una ValueError como el 'w' es redundante (lo único que puede hacer si la llamada tiene éxito es escribir en el archivo de todos modos, no puede haber existido si la llamada tiene éxito):

>>> f1 = open('new_binary_file', 'xb')
>>> f2 = open('new_text_file', 'x')

Para Python 3.2 y versiones posteriores (incluido Python 2.x), consulte la respuesta aceptada.


47
2017-08-27 20:26



Este código creará fácilmente un ARCHIVO si no existe.

import os
if not os.path.exists('file'):
    open('file', 'w').close() 

3
2018-03-14 04:19