ユーザーが読み取りおよび書き込み可能なファイル(_0600
_)を作成しようとしています。
os.open()
を次のように使用してこれを行う唯一の方法はありますか?
_import os
fd = os.open('/path/to/file', os.O_WRONLY, 0o600)
myFileObject = os.fdopen(fd)
myFileObject.write(...)
myFileObject.close()
_
理想的には、with
キーワードを使用して、オブジェクトを自動的に閉じることができるようにします。上記のことを行うより良い方法はありますか?
どうしたの? file.close()
は、os.open()
で開いていたファイルを閉じます。
with os.fdopen(os.open('/path/to/file', os.O_WRONLY | os.O_CREAT, 0o600), 'w') as handle:
handle.write(...)
この回答は、 vartecによる回答 に関する複数の懸念、特に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')
目的のモードが0600
の場合、8進数0o600
としてより明確に指定できます。さらに良いのは、stat
モジュールを使用するだけです。
古いファイルが最初に削除されたとしても、競合状態は依然として可能です。フラグにos.O_EXCL
とともにos.O_CREAT
を含めると、競合状態が原因でファイルが存在する場合、ファイルが作成されなくなります。これは、潜在的に昇格されたmode
で既に存在する可能性があるファイルを開くことを防ぐために必要な二次的なセキュリティ対策です。 Python 3、[Errno 17]のFileExistsError
は、ファイルが存在する場合に発生します。
最初にumask
を0
または0o777 ^ mode
に設定しないと、os.open
によって誤ったmode
(許可)が設定される可能性があります。これは、デフォルトのumask
が通常0
ではなく、指定されたmode
に適用されるためです。たとえば、元のumask
が2
である場合、つまり0o002
であり、指定したモードが0o222
である場合、最初にumask
の設定に失敗すると、代わりにmode
of 0o220
を持つことができますが、これは私が望んでいたものではありません。 man 2 open
ごとに、作成されたファイルのモードはmode & ~umask
です。
umask
は、できるだけ早く元の値に復元されます。この取得と設定はスレッドセーフではないため、threading.Lock
をマルチスレッドアプリケーションで使用する必要があります。
Umaskの詳細については、 this thread を参照してください。
問題は、ファイルが誰でも読み取り可能にならないように許可を設定することです(現在のユーザーの読み取り/書き込みのみ)。
残念ながら、コード自体は次のとおりです。
fd = os.open('/path/to/file', os.O_WRONLY, 0o600)
許可が世界に対して拒否されることを保証しません。現在のユーザーにr/wを設定しようとします(umaskで許可されている場合)、それだけです!
2つの非常に異なるテストシステムで、このコードは、デフォルトのumaskで-rw-r--r-でファイルを作成し、-rw-rw-rw-でファイルを作成しますumask(0)これは間違いなく望ましいものではありません(深刻なセキュリティリスクをもたらします)。
ファイルにグループおよびワールド用のビットが設定されていないことを確認したい場合は、これらのビットを最初にumaskする必要があります(umaskはdenialパーミッションのことです):
os.umask(0o177)
さらに、ファイルが異なるパーミッションですでに存在しないことを100%確実にするために、最初にそれをchmod /削除する必要があります(ターゲットディレクトリに書き込みパーミッションがない場合があるため、削除はより安全です-そしてセキュリティ上の懸念がある場合、許可されていない場所にファイルを書きたくない!)、そうしないと、ハッカーがあなたの前にファイルを作成した場合、あなたの移動を見越して世界中のr/wパーミッションを持つ場合、セキュリティ上の問題があるかもしれません。その場合、os.openはそのアクセス許可をまったく設定せずにファイルを開き、ワールドr/wシークレットファイルが残ります...
必要なもの:
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)
これは、環境や構成に関係なく、-rw -------ファイルを確実に作成するための安全な方法です。そしてもちろん、必要に応じてIOErrorをキャッチして対処することができます。ターゲットディレクトリに書き込み権限がない場合、ファイルを作成できません。既に存在する場合、削除は失敗します。
A-B-Bの優れた答えを修正して、懸念をもう少し明確にすることを提案したいと思います。主な利点は、ファイルへの実際の書き込み中に他の問題とは別に、ファイル記述子を開くときに発生する例外を処理できることです。
外側のtry ... finally
ブロックは、ファイル記述子を開く際に許可とumask
の問題を処理します。内側のwith
ブロックは、Pythonファイルオブジェクトを操作する際に起こりうる例外を処理します(これは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)
書き込む代わりにファイルに追加する場合は、ファイル記述子を次のように開く必要があります。
fdesc = os.open(outfname, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
そして、このようなファイルオブジェクト:
with os.fdopen(fdesc, "a") as outf:
もちろん、他のすべての通常の組み合わせが可能です。
私は違うやり方をするでしょう。
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:
[…]
ファイル操作コードはすでにtry
-except
- heavyである傾向があります。 os.umaskのfinally
でそれをさらに悪化させることは、あなたの目をこれ以上喜ばせないでしょう。一方、独自のコンテキストマネージャをローリングするとその簡単なになり、インデントの入れ子が多少きれいになります。