在Python中编写具有特定权限的文件

时间:2011-04-11 16:21:16

标签: python file-io permissions

我正在尝试创建一个只有用户可读且可写的文件(0600)。

使用os.open()进行此操作的唯一方法是?

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

理想情况下,我希望能够使用with关键字,以便我可以自动关闭对象。有没有更好的方法来做我上面做的事情?

6 个答案:

答案 0 :(得分:36)

有什么问题? file.close()即使已使用os.open()打开文件,也会关闭该文件。

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

答案 1 :(得分:26)

此答案解决了answer by 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,则可以更明确地将其指定为八进制数0o600。更好的是,只需使用stat模块。

即使首先删除旧文件,仍然可以进行竞争条件。在标志中包含os.O_EXCLos.O_CREAT将阻止创建文件(如果由于竞争条件而存在)。这是一项必要的辅助安全措施,可防止打开可能已存在且文件可能已升高mode的文件。在Python 3中,如果文件存在,则会引发带有[Errno 17]的FileExistsError

未能先将umask设置为00o777 ^ mode,可能导致mode设置的os.open(权限)不正确。这是因为默认umask通常不是0,而是会应用于指定的mode。例如,如果原始umask20o002,而我的指定模式为0o222,如果我未能先设置umask,则会产生mode文件可以改为0o220 man 2 open,这不是我想要的。每mode & ~umask,创建的文件的模式为umask

threading.Lock会尽快恢复到原始值。此获取和设置不是线程安全的,并且必须在多线程应用程序中使用{{1}}。

有关umask的更多信息,请参阅this thread

答案 2 :(得分:12)

<强>更新 伙计们,虽然我感谢你们在这里的赞成,但我自己不得不反对我最初提出的解决方案。原因是以这种方式做事,文件确实存在时会有一段时间,无论多么小,并且没有适当的权限 - 这会留下广泛的攻击方式,甚至是错误的行为。 /> 当然,首先使用正确的权限创建文件是要走的路 - 正确性,使用Python的with只是一些糖果。

所以请把这个答案作为“什么不该做”的例子;

原帖

您可以改为使用os.chmod

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

(请注意,在Python中使用octals的方法是明确的 - 在“0o”之前加上“0o600”前缀。在Python 2.x中,它只能编写{ {1}} - 但这既误导又弃用。)

但是,如果您的安全性至关重要,您可能应该像使用0600一样创建它,并使用os.open从{{1}返回的文件描述符中检索Python文件对象}}

答案 3 :(得分:3)

问题是关于设置权限以确保文件不会是世界可读的(仅对当前用户进行读/写)。

不幸的是,就其本身而言,代码:

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

不保证权限将被拒绝到全世界。它确实尝试为当前用户设置r / w (假设umask允许),就是这样!

在两个非常不同的测试系统上,此代码使用我的默认umask创建一个带有 -rw-r - r - 的文件,并且 -rw-rw-rw - 使用umask(0),这绝对不是我们想要的(并带来严重的安全风险)。

如果你想确保文件没有为group和world设置位,你必须首先刷新这些位(记住 - umask是拒绝的权限):

os.umask(0o177)

此外,为了100%确定文件不存在具有不同权限的文件,您必须首先chmod / delete它(删除更安全,因为您可能没有目标目录中的写权限 - 如果您有安全问题,你不想写一些你不允许的文件!),否则你可能会遇到一个安全问题,如果黑客在你拥有世界范围的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 -------文件的安全方法,无论您的环境和配置如何。当然,您可以根据需要捕获并处理IOErrors。如果您在目标目录中没有写入权限,则无法创建该文件,如果该文件已存在,则删除将失败。

答案 4 :(得分:1)

我想建议修改A-B-B的优秀答案,将问题分开一些。主要优点是您可以处理在实际写入文件期间与其他问题分开打开文件描述符期间发生的异常。

外部class Company < ActiveReecord::Base include ImageHandling ... end 块在打开文件描述符时负责处理权限和try ... finally问题。内部umask块在处理Python文件对象时处理可能的异常(因为这是OP的愿望):

with

如果要附加到文件而不是写入,那么应该像这样打开文件描述符:

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)

当然所有其他常用组合都是可能的。

答案 5 :(得分:0)

我会做不同的事情。

sum(2)

文件处理代码通常已经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繁重;使用os.umask的except会使情况更糟,这不会给您带来更多的欢乐。同时,滚动您自己的上下文管理器非常容易,并且会导致缩进嵌套更整洁。