it-swarm.com.de

Schreiben Sie eine Datei mit bestimmten Berechtigungen in Python

Ich versuche, eine Datei zu erstellen, die nur vom Benutzer gelesen und geschrieben werden kann (0600).

Ist die einzige Möglichkeit, dies zu tun, indem Sie os.open() wie folgt verwenden?

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

Idealerweise möchte ich das Schlüsselwort with verwenden können, damit ich das Objekt automatisch schließen kann. Gibt es eine bessere Möglichkeit, das zu tun, was ich oben mache?

51
lfaraone

Was ist das Problem? file.close() schließt die Datei, obwohl sie mit os.open() geöffnet wurde.

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

Diese Antwort spricht mehrere Bedenken mit dem Antwort von vartec an, insbesondere das umask.

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.
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')

Wenn der gewünschte Modus 0600 Ist, kann er deutlicher als die Oktalzahl 0o600 Angegeben werden. Noch besser, verwenden Sie einfach das stat -Modul.

Auch wenn die alte Datei zuerst gelöscht wird, ist eine Race Condition immer noch möglich. Das Einfügen von os.O_EXCL Mit os.O_CREAT In die Flags verhindert, dass die Datei erstellt wird, wenn sie aufgrund einer Racebedingung existiert. Dies ist eine notwendige sekundäre Sicherheitsmaßnahme, um das Öffnen einer möglicherweise bereits vorhandenen Datei mit einem möglicherweise erhöhten mode zu verhindern. In Python 3, FileExistsError mit [Errno 17] wird ausgelöst, wenn die Datei existiert.

Wenn Sie umask nicht zuerst auf 0 Oder auf 0o777 ^ mode Setzen, kann dies dazu führen, dass os.open Eine falsche mode (Berechtigung) setzt. Dies liegt daran, dass der Standardwert umask normalerweise nicht 0 Ist und auf den angegebenen mode angewendet wird. Wenn mein ursprüngliches umask beispielsweise 2, Dh 0o002 Ist und mein angegebener Modus 0o222 Ist, kann ich das umask, die resultierende Datei kann stattdessen ein mode von 0o220 haben, was ich nicht wollte. Gemäß man 2 open Lautet der Modus der erstellten Datei mode & ~umask.

Das umask wird so schnell wie möglich auf seinen ursprünglichen Wert zurückgesetzt. Dieses Abrufen und Festlegen ist nicht threadsicher, und in einer Multithread-Anwendung muss ein threading.Lock Verwendet werden.

Weitere Informationen zu umask finden Sie unter this thread .

28
Acumenus

pdate Leute, obwohl ich mich hier für die positiven Stimmen bedanke, muss ich mich unten gegen meine ursprünglich vorgeschlagene Lösung aussprechen. Der Grund dafür ist, dass die Dinge auf diese Weise erledigt werden. Es wird eine noch so kleine Zeitspanne geben, in der die Datei vorhanden ist und nicht die richtigen Berechtigungen vorhanden sind - dies lässt weitreichende Angriffsmöglichkeiten und sogar fehlerhaftes Verhalten offen.
Natürlich ist es der erste Weg, die Datei mit den richtigen Berechtigungen zu erstellen - entgegen der Richtigkeit ist die Verwendung von Pythons with nur eine Süßigkeit.

Nehmen Sie diese Antwort als ein Beispiel für "was nicht zu tun ist".

rsprünglicher Beitrag

Sie können stattdessen os.chmod verwenden:

>>> 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
>>>

(Beachten Sie, dass die Verwendung von Oktalen in Python explizit erfolgt, indem "0o" wie in "0o600" vorangestellt wird. In Python 2.x Es würde funktionieren, nur 0600 zu schreiben - aber das ist sowohl irreführend als auch veraltet.)

Wenn Ihre Sicherheit jedoch kritisch ist, sollten Sie wahrscheinlich auf die Erstellung mit os.open zurückgreifen und os.fdopen verwenden, um ein Python - Dateiobjekt aus dem Dateideskriptor abzurufen zurückgegeben von os.open.

12
jsbueno

Bei der Frage geht es darum, die Berechtigungen so festzulegen, dass sichergestellt ist, dass die Datei nicht für die ganze Welt lesbar ist (, nur Lesen/Schreiben für den aktuellen Benutzer ).

Leider ist der Code für sich genommen:

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

garantiert nicht, dass der Welt Berechtigungen verweigert werden . Es wird versucht, r/w für den aktuellen Benutzer festzulegen (vorausgesetzt, umask erlaubt dies), das wars!

Auf zwei sehr unterschiedlichen Testsystemen erstellt dieser Code eine Datei mit - rw-r - r - mit meiner Standard-umask und - rw-rw-rw - mit umask (0), was definitiv nicht erwünscht ist (und ein ernstes Sicherheitsrisiko darstellt).

Wenn Sie sicherstellen möchten, dass in der Datei keine Bits für Gruppe und Welt festgelegt sind, müssen Sie diese Bits zuerst umaskieren (denken Sie daran, dass umask Verweigerung von Berechtigungen ist):

os.umask(0o177)

Um 100% sicher zu sein, dass die Datei noch nicht mit unterschiedlichen Berechtigungen existiert, müssen Sie sie zuerst chmod/delete (Löschen ist sicherer, da Sie möglicherweise keine Schreibberechtigungen im Zielverzeichnis haben - und wenn Sie Sicherheitsbedenken haben) , Sie möchten keine Datei schreiben, für die Sie keine Berechtigung haben!), andernfalls besteht möglicherweise ein Sicherheitsproblem, wenn ein Hacker die Datei mit weltweiten Schreib-/Leseberechtigungen im Vorgriff auf Ihren Umzug erstellt hat. In diesem Fall öffnet os.open die Datei, ohne dass die Berechtigungen festgelegt wurden, und Sie haben eine geheime Datei ...

Also brauchst du:

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)

Dies ist der sichere Weg, um die Erstellung einer -rw ------- Datei unabhängig von Ihrer Umgebung und Konfiguration zu gewährleisten. Und natürlich können Sie die IOErrors bei Bedarf abfangen und bearbeiten. Wenn Sie keine Schreibrechte im Zielverzeichnis haben, sollten Sie die Datei nicht erstellen können. Wenn sie bereits vorhanden ist, schlägt das Löschen fehl.

3
jytou

Ich möchte eine Modifikation der hervorragenden Antwort von A-B-B vorschlagen, die die Bedenken etwas klarer trennt. Der Hauptvorteil wäre, dass Sie Ausnahmen, die beim Öffnen des Dateideskriptors auftreten, getrennt von anderen Problemen beim tatsächlichen Schreiben in die Datei behandeln können.

Der äußere try ... finally block kümmert sich beim Öffnen des Dateideskriptors um die Behandlung der Erlaubnis- und umask Probleme. Der innere with -Block behandelt mögliche Ausnahmen während der Arbeit mit dem Python -Dateiobjekt (da dies der Wunsch des OP war):

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)

Wenn Sie an die Datei anhängen möchten, anstatt sie zu schreiben, sollte der Dateideskriptor folgendermaßen geöffnet werden:

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

und das Dateiobjekt so:

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

Selbstverständlich sind auch alle anderen üblichen Kombinationen möglich.

1
Laryx Decidua

Ich würde es anders machen.

from contextlib import contextmanager

@contextmanager
def umask_helper(desired_umask):
    """ A little helper to safely set and restore umask(2). """
    try:
        prev_umask = os.umask(desired_umask)
        yield
    finally:
        os.umask(prev_umask)

# ---------------------------------- […] ---------------------------------- #

        […]

        with umask_helper(0o077):
            os.mkdir(os.path.dirname(MY_FILE))
            with open(MY_FILE, 'wt') as f:
                […]

Datei-manipulierender Code ist in der Regel bereits try-except- schwer; Es noch schlimmer zu machen mit os.umask's finally wird deine Augen nicht mehr erfreuen. In der Zwischenzeit ist das Rollen des eigenen Kontextmanagers so einfach und führt zu einer etwas übersichtlicheren Verschachtelung von Einrückungen.

0
ulidtko