2011-04-11 20 views
43

Estoy intentando crear un archivo que solo sea legible por el usuario y escribible (0600).Escribir archivo con permisos específicos en Python

¿Es la única manera de hacerlo utilizando os.open() de la siguiente manera?

import os 
fd = os.open('/path/to/file', os.O_WRONLY, 0o600) 
myFileObject = os.fdopen(fd) 
myFileObject.write(...) 
myFileObject.close() 

Idealmente, me gustaría ser capaz de utilizar la palabra clave with para que pueda cerrar el objeto automáticamente. ¿Hay una mejor manera de hacer lo que estoy haciendo arriba?

Respuesta

34

¿Cuál es el problema? file.close() cerrará el archivo aunque estuvo abierto con os.open().

with os.fdopen(os.open('/path/to/file', os.O_WRONLY | os.O_CREAT, 0o600), 'w') as handle: 
    handle.write(...) 
+3

Considero que esta respuesta es mejor que la mía, pero no es "cuál es el problema": presenta un factor nuevo que el OP no sabe: es la conversión de un manejador de archivos en un objeto de archivo Python – jsbueno

+0

@jsbueno: I ' Solo he combinado las dos primeras líneas y he usado 'con'. Y en el ejemplo en la pregunta, el archivo se cierra a través de 'myFileObject.close()' de todos modos. – vartec

+1

Esto no funciona para mí. os.open con estos flags espera que el archivo ya exista. >>> f = os.open ('test.txt', os.O_WRONLY, 0600) Traceback (última llamada más reciente): Archivo "", línea 1, en OSError: [Errno 2] No existe dicho archivo o directorio: 'test.txt' –

11

actualización Amigos, aunque les agradezco los votos a favor aquí, yo mismo tengo que argumentar en contra de mi solución originalmente propuesta a continuación. La razón es hacer las cosas de esta manera, habrá una cantidad de tiempo, por pequeña que sea, donde exista el archivo, y no tenga los permisos adecuados en su lugar, esto dejará abiertas amplias formas de ataque e incluso un comportamiento defectuoso.
Por supuesto, crear el archivo con los permisos correctos en primer lugar es el camino a seguir; en contra de la exactitud de eso, usar with de Python es solo un caramelo.

Por lo tanto, tome esta respuesta como un ejemplo de "qué no hacer";

post original

Puede utilizar os.chmod lugar:

>>> import os 
>>> name = "eek.txt" 
>>> with open(name, "wt") as myfile: 
... os.chmod(name, 0o600) 
... myfile.write("eeek") 
... 
>>> os.system("ls -lh " + name) 
-rw------- 1 gwidion gwidion 4 2011-04-11 13:47 eek.txt 
0 
>>> 

(Tenga en cuenta que la forma de utilizar octales en Python es por ser explícita - prefijándolo con "0o" como en " 0o600 ". En Python 2.x funcionaría escribir solo 0600, pero eso es engañoso y obsoleto.)

Sin embargo , si su seguridad es crítica, probablemente debería recurrir a crearla con os.open, como lo hace y usar os.fdopen para recuperar un objeto de archivo Python del descriptor de archivo devuelto por os.open.

+5

Código de Racy es malo – SaveTheRbtz

+1

Sé que no es la pregunta, pero el mismo código es útil para establecer el propietario del archivo y el grupo sobre la marcha simplemente cambie la línea chmod para os.chown (name, uid, gid). – alemol

23

Esta respuesta se dirige a múltiples preocupaciones con el answer by vartec, especialmente el umask preocupación.

import os 
import stat 

# Define file params 
fname = '/tmp/myfile' 
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL # Refer to "man 2 open". 
mode = stat.S_IRUSR | stat.S_IWUSR # This is 0o600 in octal. 
umask = 0o777^mode # Prevents always downgrading umask to 0. 

# For security, remove file with potentially elevated mode 
try: 
    os.remove(fname) 
except OSError: 
    pass 

# Open file descriptor 
umask_original = os.umask(umask) 
try: 
    fdesc = os.open(fname, flags, mode) 
finally: 
    os.umask(umask_original) 

# Open file handle and write to file 
with os.fdopen(fdesc, 'w') as fout: 
    fout.write('something\n') 

Si el modo deseado es 0600, más claramente se puede especificar como el número octal 0o600. Aún mejor, solo use el módulo stat.

Aunque el archivo anterior se eliminó por primera vez, aún es posible una condición de carrera. Incluir os.O_EXCL con os.O_CREAT en los indicadores evitará que el archivo se cree si existe debido a una condición de carrera. Esta es una medida de seguridad secundaria necesaria para evitar la apertura de un archivo que ya existe con un mode potencialmente elevado. En Python 3, se genera FileExistsError con [Errno 17] si el archivo existe.

Si no se establece por primera vez la umask-0 o para 0o777^mode puede conducir a una incorrecta mode (permiso) está fijado por os.open. Esto se debe a que el valor predeterminado umask generalmente no es 0, y se aplicará al mode especificado. Por ejemplo, si mi original umask es decir 20o002, y mi modo especificado es 0o222, si fracaso para establecer la primera umask, el archivo resultante puede tener lugar una mode de 0o220, que no es lo que quería. Por man 2 open, el modo del archivo creado es mode & ~umask.

El umask se restaura a su valor original tan pronto como sea posible. Esta obtención y configuración no es segura para subprocesos, y se debe usar threading.Lock en una aplicación multiproceso. Para obtener más información sobre umask, consulte this thread.

+1

Muchas gracias por abordar el problema 'umask', resolvió mi problema. – Nobilis

+1

¡Gracias por esta respuesta! No pude entender por qué cada archivo que guardé de python obtuvo 0600 permisos de archivo, incluso cuando se usa '' os.open (mode = 0666) ''. Esto resolvió el problema. – ostrokach

+1

Esta respuesta me desconcertó un poco al hacer referencia a XOR. umask no hace XOR. Sin embargo, establecer umask en 0 es correcto si quiere asegurarse de que los permisos del archivo sean exactamente los mismos que los especificados, y no algo con menos bits configurados debido a la umask del usuario. Si su objetivo es garantizar que el archivo no sea legible/editable en todo el mundo, no necesita configurar umask. – Nelson

1

Me gustaría sugerir una modificación de la excelente respuesta de A-B-B que separa las preocupaciones un poco más claramente. La principal ventaja sería que puede manejar las excepciones que se producen durante la apertura del descriptor de archivo por separado de otros problemas durante la escritura real en el archivo.

El bloque externo try ... finally se ocupa de gestionar el permiso y umask problemas al abrir el descriptor de archivo. Los interiores with ofertas de bloque con posibles excepciones al trabajar con el objeto de archivo de Python (ya que éste era el deseo de la OP):

try: 
    oldumask = os.umask(0) 
    fdesc = os.open(outfname, os.O_WRONLY | os.O_CREAT, 0o600) 
    with os.fdopen(fdesc, "w") as outf: 
     # ...write to outf, closes on success or on exceptions automatically... 
except IOError, ... : 
    # ...handle possible os.open() errors here... 
finally: 
    os.umask(oldumask) 

Si desea anexar al archivo en vez de escribir, entonces el descriptor de archivo se deben abrir de esta manera:

fdesc = os.open(outfname, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600) 

y el objeto archivo de la siguiente manera:

with os.fdopen(fdesc, "a") as outf: 

Por supuesto, todas las demás combinaciones usuales son posibles.

+0

Agregar un archivo existente no tiene sentido en este contexto porque el 'modo 'especificado para' os.open' nunca se aplicará. El modo previamente existente preservará. –

3

La pregunta es sobre la configuración de los permisos para asegurarse de que el archivo no sea legible en todo el mundo (solo lectura/escritura para el usuario actual).

Desafortunadamente, por su cuenta, el código:

fd = os.open('/path/to/file', os.O_WRONLY, 0o600) 

no garantiza que los permisos serán niega al mundo. Garantiza que el archivo tendrá r/w para el usuario actual, ¡eso es todo!

En dos sistemas de ensayos muy diferentes, este código crea un archivo con -rw-r - r-- con mi máscara de usuario por defecto, y -rw-rw-rw- con máscara de usuario (0), que es definitivamente no es lo que se desea (y plantea un serio riesgo de seguridad).

Si desea asegurarse de que el archivo no tiene bits establecidos para el grupo y en el mundo, usted tiene que umask estos bits primero (recuerde - máscara de usuario es negación de permisos):

os.umask(0o177) 

Además, para estar 100% seguro de que el archivo no existe con diferentes permisos, primero debe modificarlo/eliminarlo (eliminar es más seguro, ya que es posible que no tenga permisos de escritura en el directorio de destino, y si tiene problemas de seguridad, no desea escribir ningún archivo donde no se le permita), de lo contrario puede tener un problema de seguridad si un pirata informático crea el archivo antes que usted con permisos de r/w en todo el mundo en previsión de su mo ve. En ese caso, os.open abrirá el archivo sin establecer sus permisos en absoluto y uno se queda con un archivo mundial r/w secreta ...

por lo que necesita:

import os 
if os.path.isfile(file): 
    os.remove(file) 
original_umask = os.umask(0o177) # 0o777^0o600 
try: 
    handle = os.fdopen(os.open(file, os.O_WRONLY | os.O_CREAT, 0o600), 'w') 
finally: 
    os.umask(original_umask) 

Esto es la forma segura de garantizar la creación de un archivo -rw ------- independientemente de su entorno y configuración. Y, por supuesto, puede atrapar y manejar los IOErrors según sea necesario. Si no tiene permisos de escritura en el directorio de destino, no debería poder crear el archivo, y si ya existía, la eliminación fallará.

+0

Después de eliminar el archivo como sugirió, un valor ideal para 'flags' puede ser' os.O_WRONLY | os.O_CREAT | os.O_EXCL'. Tenga en cuenta que 'os.O_EXCL' impide la apertura de un archivo que ya existe. –

Cuestiones relacionadas